diff --git a/.github/workflows/sonargate.yml b/.github/workflows/sonargate.yml index eb54bac56af..ad0d3711aa9 100644 --- a/.github/workflows/sonargate.yml +++ b/.github/workflows/sonargate.yml @@ -5,10 +5,10 @@ jobs: runs-on: ubuntu-20.04 steps: - uses: actions/checkout@v2 - - name: Set up Java11 + - name: Set up Java uses: actions/setup-java@v1 with: - java-version: '11' + java-version: '17' - name: Install confd run: | sudo apt-get install wget diff --git a/CHANGELOG.md b/CHANGELOG.md index 17863319fc7..ff5056aba65 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,4 +1,27 @@ # Changelog + +## v1.152.0 (19/01/2024) + +### Bug Fixes: +- [#5520](https://github.com/telstra/open-kilda/pull/5520) Add ordering for simple flow history actions (Issue: [#5519](https://github.com/telstra/open-kilda/issues/5519)) +- [#5521](https://github.com/telstra/open-kilda/pull/5521) Add parameters validation to get flow and HA-flow history API. + +### Improvements: +- [#5536](https://github.com/telstra/open-kilda/pull/5536) upgrade java version in sonargate.yml +- [#5514](https://github.com/telstra/open-kilda/pull/5514) [TEST]: 5162: Refactoring HC [**tests**] +- [#5518](https://github.com/telstra/open-kilda/pull/5518) Add default options for gradle (Issue: [#5155](https://github.com/telstra/open-kilda/issues/5155)) [**configuration**] + +### Other changes: +- [#5525](https://github.com/telstra/open-kilda/pull/5525) [TEST] Refactoring the way to choose switch pairs for test pt.2 [**tests**] +- [#5532](https://github.com/telstra/open-kilda/pull/5532) [TEST] Unignore tests which can be run [**tests**] + +For the complete list of changes, check out [the commit log](https://github.com/telstra/open-kilda/compare/v1.151.0...v1.152.0). + +### Affected Components: +history + +--- + ## v1.151.0 (02/01/2024) ### Improvements: diff --git a/confd/templates/makefile/makefile.tmpl b/confd/templates/makefile/makefile.tmpl index 8472cddcd79..7d7024d4e72 100644 --- a/confd/templates/makefile/makefile.tmpl +++ b/confd/templates/makefile/makefile.tmpl @@ -93,10 +93,10 @@ clean-sources: cd src-java && ./gradlew clean compile compile-blue: update-props-blue compile-common - cd src-java && ./gradlew buildAndCopyArtifacts -PdestPath=../docker/BUILD --info --stacktrace $(GRADLE_COMPILE_PARAMS) + cd src-java && ./gradlew buildAndCopyArtifacts -PdestPath=../docker/BUILD $(GRADLE_COMPILE_PARAMS) compile-green: update-props-green compile-common - cd src-java && ./gradlew buildAndCopyArtifacts -PdestPath=../docker/BUILD --info --stacktrace $(GRADLE_COMPILE_PARAMS) + cd src-java && ./gradlew buildAndCopyArtifacts -PdestPath=../docker/BUILD $(GRADLE_COMPILE_PARAMS) compile-common: $(MAKE) -C src-python/lab-service/lab test diff --git a/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/service/history/FlowHistoryService.java b/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/service/history/FlowHistoryService.java index a3b0f122a0d..a520d64da50 100644 --- a/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/service/history/FlowHistoryService.java +++ b/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/service/history/FlowHistoryService.java @@ -43,7 +43,6 @@ @Slf4j public final class FlowHistoryService { - private Instant lastHistoryEntryTime; private final HistoryUpdateCarrier carrier; private FlowHistoryService(HistoryUpdateCarrier carrier) { @@ -72,7 +71,7 @@ public void save(HaFlowHistory parameters) { .haFlowDumpData(parameters.getFlowDumpData()) .haFlowHistoryData(HaFlowHistoryData.builder() .action(parameters.getAction()) - .time(getNextHistoryEntryTime()) + .time(Instant.now()) .description(parameters.getDescription()) .haFlowId(haFlowId) .build()) @@ -102,7 +101,7 @@ public void save(FlowHistory parameters) { .flowDumpData(parameters.getFlowDumpData()) .flowHistoryData(FlowHistoryData.builder() .action(parameters.getAction()) - .time(getNextHistoryEntryTime()) + .time(Instant.now()) .description(parameters.getDescription()) .flowId(flowId) .build()) @@ -121,7 +120,7 @@ public void save(FlowHistory parameters) { */ public boolean saveNewHaFlowEvent(HaFlowEventData eventData) { try { - Instant timestamp = getNextHistoryEntryTime(); + Instant timestamp = Instant.now(); String haFlowId = Objects.requireNonNull(eventData.getHaFlowId()); String taskId = Objects.requireNonNull(eventData.getTaskId()); @@ -158,7 +157,7 @@ public boolean saveNewHaFlowEvent(HaFlowEventData eventData) { */ public boolean saveNewFlowEvent(FlowEventData eventData) { try { - Instant timestamp = getNextHistoryEntryTime(); + Instant timestamp = Instant.now(); String flowId = Objects.requireNonNull(eventData.getFlowId()); String taskId = Objects.requireNonNull(eventData.getTaskId()); @@ -208,19 +207,4 @@ public void saveError(FlowHistory parameters) { log.error("An error occurred when trying to save an error to History", e); } } - - /** - * This is a workaround to preserve ordering when there are fast consecutive calls. - * // TODO decide if it worth to replace it with IDs of the events in this stream of events. - * @return possibly adjusted time of the event - */ - private Instant getNextHistoryEntryTime() { - Instant now = Instant.now(); - if (lastHistoryEntryTime == null || lastHistoryEntryTime.isBefore(now)) { - lastHistoryEntryTime = now; - } else { - lastHistoryEntryTime = lastHistoryEntryTime.plusMillis(1); - } - return lastHistoryEntryTime; - } } diff --git a/src-java/build.gradle b/src-java/build.gradle index c09db37f569..a04a445dbda 100644 --- a/src-java/build.gradle +++ b/src-java/build.gradle @@ -29,6 +29,7 @@ subprojects { tasks.withType(JavaCompile) { options.encoding = 'UTF-8' + options.fork = true } repositories { diff --git a/src-java/gradle.properties b/src-java/gradle.properties new file mode 100644 index 00000000000..e3e971c5e92 --- /dev/null +++ b/src-java/gradle.properties @@ -0,0 +1,26 @@ +# Default parameters for Gradle tasks in the root project are propagated to the downstream projects. +# These default parameters could be overridden by passing arguments to the make command in the following way: +# make build-stable GRADLE_COMPILE_PARAMS="-x test -q" + +# Execute Gradle as a daemon. +org.gradle.daemon=true + +# Equivalent to --build-cache; this enables caching, significantly speeding up consecutive runs. +org.gradle.caching=true + +# Maximum memory set to 4G. If OutOfMemoryError persists, consider increasing the value and adding the -Xms4g parameter. +org.gradle.jvmargs=-Xmx4g + +# This parameter performs the configuration phase before all other tasks and only when needed, optimizing Gradle for not so big projects. +org.gradle.configureondemand=true + +# Enable parallel execution of Gradle tasks. Max workers set to 7, optimal for most modern PCs with hyper-threading and more than 4 physical cores. +# This configuration ensures at least 1 core remains for OS purposes, preventing console glitches, SSH connection drops, and similar issues. +org.gradle.parallel=true +org.gradle.workers.max=7 + +# Suppress gradle welcome messages. +org.gradle.welcome=never + +# Display only info messages. Alternatively, one could use the 'quiet' level to reduce the output. +org.gradle.logging.level=info diff --git a/src-java/kilda-persistence-hibernate/src/main/java/org/openkilda/persistence/hibernate/entities/history/HibernateFlowEvent.java b/src-java/kilda-persistence-hibernate/src/main/java/org/openkilda/persistence/hibernate/entities/history/HibernateFlowEvent.java index 68804c1e01f..cb2462aad64 100644 --- a/src-java/kilda-persistence-hibernate/src/main/java/org/openkilda/persistence/hibernate/entities/history/HibernateFlowEvent.java +++ b/src-java/kilda-persistence-hibernate/src/main/java/org/openkilda/persistence/hibernate/entities/history/HibernateFlowEvent.java @@ -74,8 +74,14 @@ public class HibernateFlowEvent extends EntityBase implements FlowEventData { @Column(name = "unstructured", columnDefinition = "json") private FlowEventUnstructured unstructured; + /** + * The ordering relies on grouping messages in topologies: history messages for the same correlation ID arrive to + * the same worker and processed sequentially within a single operation (see fieldsGrouping in topology builders). + * However, timestamps are not generated by DB, so assumptions about the relation between timestamp and + * id cannot be made. + */ @OneToMany(mappedBy = "event", cascade = CascadeType.ALL) - @OrderBy("event_time") + @OrderBy("eventTime , id") private List actions = new ArrayList<>(); @OneToMany(mappedBy = "event", cascade = CascadeType.ALL) diff --git a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/utils/flowhistory/FlowHistoryRangeConstraints.java b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/utils/flowhistory/FlowHistoryRangeConstraints.java index f9be12a5071..1c5190ed27d 100644 --- a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/utils/flowhistory/FlowHistoryRangeConstraints.java +++ b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/utils/flowhistory/FlowHistoryRangeConstraints.java @@ -46,6 +46,13 @@ public class FlowHistoryRangeConstraints { public FlowHistoryRangeConstraints(Optional timeFromInput, Optional timeToInput, Optional maxCountInput) { + if (isTimeFromAfterTimeTo(timeFromInput, timeToInput)) { + throw new MessageException(RequestCorrelationId.getId(), System.currentTimeMillis(), + ErrorType.PARAMETERS_INVALID, format("Invalid 'timeFrom' and 'timeTo' arguments: %s and %s", + timeFromInput.get(), timeToInput.get()), + "'timeFrom' must be less than or equal to 'timeTo'"); + } + this.timeFrom = timeFromInput.map(LongToInstantConverter::convert).orElseGet(() -> Instant.ofEpochSecond(0L)); this.timeTo = timeToInput.map(LongToInstantConverter::convert).orElseGet(Instant::now); @@ -66,7 +73,11 @@ ErrorType.PARAMETERS_INVALID, format("Invalid `max_count` argument '%s'.", contentRangeRequiredPredicate = size -> (!maxCountInput.isPresent() && !timeFromInput.isPresent() && !timeToInput.isPresent() - && size == DEFAULT_MAX_HISTORY_RECORD_COUNT); + && size > DEFAULT_MAX_HISTORY_RECORD_COUNT); + } + + private boolean isTimeFromAfterTimeTo(Optional timeFromInput, Optional timeToInput) { + return timeFromInput.isPresent() && timeToInput.isPresent() && timeFromInput.get() > timeToInput.get(); } public boolean isContentRangeRequired(int size) { diff --git a/src-java/northbound-service/northbound/src/test/java/org/openkilda/northbound/utils/flowhistory/FlowHistoryRangeConstraintsTest.java b/src-java/northbound-service/northbound/src/test/java/org/openkilda/northbound/utils/flowhistory/FlowHistoryRangeConstraintsTest.java new file mode 100644 index 00000000000..0606da971bd --- /dev/null +++ b/src-java/northbound-service/northbound/src/test/java/org/openkilda/northbound/utils/flowhistory/FlowHistoryRangeConstraintsTest.java @@ -0,0 +1,90 @@ +/* Copyright 2023 Telstra Open Source + * + * 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 org.openkilda.northbound.utils.flowhistory; + +import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import org.openkilda.messaging.error.MessageException; + +import org.junit.jupiter.api.Test; + +import java.time.Instant; +import java.util.Optional; + +class FlowHistoryRangeConstraintsTest { + @Test + void validateTimeParametersTest() { + assertThrows(MessageException.class, () -> + new FlowHistoryRangeConstraints(Optional.of(300L), Optional.of(20L), Optional.empty())); + + assertDoesNotThrow(() -> + new FlowHistoryRangeConstraints(Optional.of(20L), Optional.of(300L), Optional.empty())); + + assertDoesNotThrow(() -> + new FlowHistoryRangeConstraints(Optional.empty(), Optional.of(300L), Optional.empty())); + + assertDoesNotThrow(() -> + new FlowHistoryRangeConstraints(Optional.of(20L), Optional.empty(), Optional.empty())); + } + + @Test + void whenNoTimeParameterProvided_defaultIsSet() { + FlowHistoryRangeConstraints f1 = new FlowHistoryRangeConstraints( + Optional.empty(), Optional.of(300L), Optional.empty()); + assertEquals(Instant.ofEpochSecond(0L), f1.getTimeFrom()); + + FlowHistoryRangeConstraints f2 = new FlowHistoryRangeConstraints( + Optional.of(300L), Optional.empty(), Optional.empty()); + assertTrue(f2.getTimeTo().isBefore(Instant.now().plusNanos(1))); + + FlowHistoryRangeConstraints f3 = new FlowHistoryRangeConstraints( + Optional.empty(), Optional.empty(), Optional.empty()); + assertEquals(Instant.ofEpochSecond(0L), f3.getTimeFrom()); + assertTrue(f3.getTimeTo().isBefore(Instant.now().plusNanos(1))); + } + + @Test + void maxCountTest() { + assertThrows(MessageException.class, () -> + new FlowHistoryRangeConstraints(Optional.empty(), Optional.empty(), Optional.of(-5))); + + assertDoesNotThrow(() -> + new FlowHistoryRangeConstraints(Optional.empty(), Optional.empty(), Optional.empty())); + + assertDoesNotThrow(() -> + new FlowHistoryRangeConstraints(Optional.empty(), Optional.empty(), Optional.of(12))); + } + + @Test + void contentRangeRequiredTest() { + FlowHistoryRangeConstraints f1 = new FlowHistoryRangeConstraints( + Optional.empty(), Optional.of(300L), Optional.empty()); + assertFalse(f1.isContentRangeRequired(142)); + FlowHistoryRangeConstraints f2 = new FlowHistoryRangeConstraints( + Optional.of(300L), Optional.empty(), Optional.empty()); + assertFalse(f2.isContentRangeRequired(142)); + FlowHistoryRangeConstraints f3 = new FlowHistoryRangeConstraints( + Optional.empty(), Optional.empty(), Optional.of(1)); + assertFalse(f3.isContentRangeRequired(142)); + FlowHistoryRangeConstraints f4 = new FlowHistoryRangeConstraints( + Optional.empty(), Optional.empty(), Optional.empty()); + assertTrue(f4.isContentRangeRequired(142)); + } +} diff --git a/src-java/testing/functional-tests/build.gradle b/src-java/testing/functional-tests/build.gradle index 9c9ea343a09..bbb19573721 100644 --- a/src-java/testing/functional-tests/build.gradle +++ b/src-java/testing/functional-tests/build.gradle @@ -100,7 +100,7 @@ tasks.withType(Test) { } retry { //test-retry plugin config maxRetries = 2 - maxFailures = 30 + maxFailures = 50 failOnPassedAfterRetry = false } } diff --git a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/error/AbstractExpectedError.groovy b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/error/AbstractExpectedError.groovy index d5ebdb9506c..3859dfdf7e9 100644 --- a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/error/AbstractExpectedError.groovy +++ b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/error/AbstractExpectedError.groovy @@ -3,6 +3,7 @@ package org.openkilda.functionaltests.error import org.openkilda.messaging.error.MessageError import org.springframework.http.HttpStatus import org.springframework.web.client.HttpClientErrorException +import org.springframework.web.client.HttpStatusCodeException import java.util.regex.Pattern @@ -18,7 +19,7 @@ abstract class AbstractExpectedError extends HttpClientErrorException{ this.descriptionPattern = messagePattern } - boolean matches(HttpClientErrorException exception) { + boolean matches(HttpStatusCodeException exception) { MessageError messageError = exception.responseBodyAsString.to(MessageError) assert exception.statusCode == this.statusCode assert messageError.getErrorMessage() == this.message diff --git a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/error/flow/FlowEndpointsNotSwappedExpectedError.groovy b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/error/flow/FlowEndpointsNotSwappedExpectedError.groovy new file mode 100644 index 00000000000..813bc6dcff1 --- /dev/null +++ b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/error/flow/FlowEndpointsNotSwappedExpectedError.groovy @@ -0,0 +1,15 @@ +package org.openkilda.functionaltests.error.flow + +import org.openkilda.functionaltests.error.AbstractExpectedError +import org.springframework.http.HttpStatus + +import java.util.regex.Pattern + +class FlowEndpointsNotSwappedExpectedError extends AbstractExpectedError{ + final static HttpStatus statusCode = HttpStatus.INTERNAL_SERVER_ERROR + final static String message = "Could not swap endpoints" + + FlowEndpointsNotSwappedExpectedError(Pattern descriptionPattern) { + super(statusCode, message, descriptionPattern) + } +} diff --git a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/extension/env/EnvExtension.groovy b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/extension/env/EnvExtension.groovy index e46c5212fd4..e4785746878 100644 --- a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/extension/env/EnvExtension.groovy +++ b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/extension/env/EnvExtension.groovy @@ -1,9 +1,13 @@ package org.openkilda.functionaltests.extension.env +import static groovyx.gpars.GParsExecutorsPool.withPool +import static org.openkilda.model.MeterId.MAX_SYSTEM_RULE_METER_ID import static org.openkilda.testing.Constants.SWITCHES_ACTIVATION_TIME import static org.openkilda.testing.Constants.TOPOLOGY_DISCOVERING_TIME import static org.openkilda.testing.Constants.WAIT_OFFSET +import static org.openkilda.testing.Constants.HEALTH_CHECK_TIME +import org.openkilda.functionaltests.exception.IslNotFoundException import org.openkilda.functionaltests.extension.spring.SpringContextListener import org.openkilda.functionaltests.extension.spring.SpringContextNotifier import org.openkilda.functionaltests.helpers.SwitchHelper @@ -16,11 +20,14 @@ import org.openkilda.model.FlowEncapsulationType import org.openkilda.model.SwitchFeature import org.openkilda.northbound.dto.v1.links.LinkParametersDto import org.openkilda.testing.model.topology.TopologyDefinition +import org.openkilda.testing.model.topology.TopologyDefinition.Status import org.openkilda.testing.service.floodlight.model.Floodlight import org.openkilda.testing.service.labservice.LabService import org.openkilda.testing.service.lockkeeper.LockKeeperService import org.openkilda.testing.service.northbound.NorthboundService import org.openkilda.testing.service.northbound.NorthboundServiceV2 +import org.openkilda.testing.tools.IslUtils +import org.openkilda.testing.tools.SoftAssertions import org.openkilda.testing.tools.TopologyPool import groovy.util.logging.Slf4j @@ -29,6 +36,7 @@ import org.springframework.beans.factory.annotation.Autowired import org.springframework.beans.factory.annotation.Qualifier import org.springframework.beans.factory.annotation.Value import org.springframework.context.ApplicationContext +import spock.lang.Shared import java.util.concurrent.TimeUnit @@ -59,7 +67,10 @@ class EnvExtension extends AbstractGlobalExtension implements SpringContextListe LockKeeperService lockKeeper @Autowired - List floodlights + List commonFloodlights + + @Autowired @Shared + IslUtils islUtils @Value('${spring.profiles.active}') String profile @@ -70,6 +81,9 @@ class EnvExtension extends AbstractGlobalExtension implements SpringContextListe @Value('${rebuild.topology_lab:true}') boolean isTopologyRebuildRequired + @Value('${health_check.verifier:true}') + boolean enable + @Override void start() { SpringContextNotifier.addListener(this) @@ -88,6 +102,7 @@ class EnvExtension extends AbstractGlobalExtension implements SpringContextListe throw new RuntimeException("Provided profile '$profile' is unknown. Select one of the following profiles:" + " hardware, virtual") } + isHealthCheckRequired(profile, enable, isTopologyRebuildRequired) && verifyTopologyReadiness(topology) } void buildVirtualEnvironment() { @@ -143,7 +158,7 @@ class EnvExtension extends AbstractGlobalExtension implements SpringContextListe def lab = labService.createLab(topo) //can't create in parallel, hangs on Jenkins. why? topo.setLabId(lab.labId) TimeUnit.SECONDS.sleep(3) //container with topology needs some time to fully start, this is async - !unblocked && lockKeeper.removeFloodlightAccessRestrictions(floodlights*.region) + !unblocked && lockKeeper.removeFloodlightAccessRestrictions(commonFloodlights*.region) unblocked = true //wait until topology is discovered @@ -181,6 +196,107 @@ class EnvExtension extends AbstractGlobalExtension implements SpringContextListe server42IslRtt = (sw.prop.server42IslRtt == null ? "AUTO" : (sw.prop.server42IslRtt ? "ENABLED" : "DISABLED")) }) } + verifyTopologyReadiness(topo) + } + } + + Closure allSwitchesAreActive = { TopologyDefinition topologyDefinition -> + assert northbound.activeSwitches.switchId.containsAll(topologyDefinition.switches.findAll { it.status != Status.Inactive }.dpId) + } + + Closure allLinksAreActive = { TopologyDefinition topologyDefinition -> + def links = northbound.getAllLinks().findAll { it.source.switchId in topologyDefinition.activeSwitches.dpId } + assert links.findAll { it.state != IslChangeType.DISCOVERED }.empty + + def topoLinks = topologyDefinition.islsForActiveSwitches.collectMany { isl -> + [islUtils.getIslInfo(links, isl).orElseThrow { new IslNotFoundException(isl.toString()) }, + islUtils.getIslInfo(links, isl.reversed).orElseThrow { + new IslNotFoundException(isl.reversed.toString()) + }] + } + def missingLinks = links.findAll { it.state == IslChangeType.DISCOVERED } - topoLinks + assert missingLinks.empty, "These links are missing in topology.yaml" + } + + + Closure noFlowsLeft = { TopologyDefinition topologyDefinition -> + assert northboundV2.allFlows.findAll { it.source.switchId in topologyDefinition.activeSwitches.dpId }.empty, "There are flows left from previous tests" + } + + Closure noLinkPropertiesLeft = { TopologyDefinition topologyDefinition -> + assert northbound.getAllLinkProps().findAll { it.srcSwitch in topologyDefinition.activeSwitches.dpId.collect { it.toString() } }.empty + } + + Closure linksBandwidthAndSpeedMatch = { TopologyDefinition topologyDefinition -> + def speedBwAssertions = new SoftAssertions() + def links = northbound.getAllLinks().findAll { it.source.switchId in topologyDefinition.activeSwitches.dpId } + speedBwAssertions.checkSucceeds { assert links.findAll { it.availableBandwidth != it.speed }.empty } + speedBwAssertions.verify() + } + + Closure noExcessRulesMeters = { TopologyDefinition topologyDefinition -> + def excessRulesAssertions = new SoftAssertions() + withPool { + (topologyDefinition.activeSwitches).eachParallel { sw -> + def rules = northbound.validateSwitchRules(sw.dpId) + excessRulesAssertions.checkSucceeds { assert rules.excessRules.empty, sw } + excessRulesAssertions.checkSucceeds { assert rules.missingRules.empty, sw } + if (!sw.virtual && sw.ofVersion != "OF_12") { + excessRulesAssertions.checkSucceeds { + assert northbound.getAllMeters(sw.dpId).meterEntries.findAll { + it.meterId > MAX_SYSTEM_RULE_METER_ID + }.isEmpty(), "Switch has meters above system max ones" + } + } + } + excessRulesAssertions.verify() + } + } + + Closure allSwitchesConnectedToExpectedRegion = { TopologyDefinition topologyDefinition -> + def regionVerifications = new SoftAssertions() + commonFloodlights.forEach { fl -> + def expectedSwitchIds = topologyDefinition.activeSwitches.findAll { fl.region in it.regions }*.dpId + if (!expectedSwitchIds.empty) { + regionVerifications.checkSucceeds { + assert fl.floodlightService.switches.switchId.containsAll(expectedSwitchIds) + } + } + } + regionVerifications.verify() + } + + Closure switchesConfigurationIsCorrect = { TopologyDefinition topologyDefinition -> + def switchConfigVerification = new SoftAssertions() + withPool { + topologyDefinition.activeSwitches.eachParallel { sw -> + switchConfigVerification.checkSucceeds { + //server42 props can be either on or off + assert northbound.getSwitchProperties(sw.dpId).multiTable + } + } } + switchConfigVerification.verify() + } + + boolean isHealthCheckRequired(String profile, Boolean enable, Boolean isTopologyRebuildRequired) { + return (profile == "hardware" && enable) || (profile == "virtual" && !isTopologyRebuildRequired && enable) + } + + void verifyTopologyReadiness(TopologyDefinition topologyDefinition) { + log.info("Starting topology-related HC: lab_id=" + topologyDefinition.labId) + Wrappers.wait(HEALTH_CHECK_TIME) { + withPool { + [allSwitchesAreActive, + allLinksAreActive, + profile == "hardware" || !isTopologyRebuildRequired ? noFlowsLeft : null, + profile == "hardware" || !isTopologyRebuildRequired ? noLinkPropertiesLeft: null, + linksBandwidthAndSpeedMatch, + noExcessRulesMeters, + allSwitchesConnectedToExpectedRegion, + switchesConfigurationIsCorrect].findAll().eachParallel { it(topologyDefinition) } + } + } + log.info("Topology-related HC passed: lab_id=" + topologyDefinition.labId) } } diff --git a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/TopologyHelper.groovy b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/TopologyHelper.groovy index f9bc5d4317c..5eaec91f18f 100644 --- a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/TopologyHelper.groovy +++ b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/TopologyHelper.groovy @@ -42,49 +42,6 @@ class TopologyHelper { @Autowired PathHelper pathHelper - List getAllSwitchPairs(boolean includeReverse = true) { - return getSwitchPairs(includeReverse) - } - - List getAllSingleSwitchPairs() { - return topology.activeSwitches.collect { SwitchPair.singleSwitchInstance(it) } - } - - /** - * @deprecated Use new mechanism from org.openkilda.functionaltests.helpers.model.SwitchPairs class - */ - @Deprecated - List getAllNeighboringSwitchPairs() { - getSwitchPairs().findAll { - it.paths.min { it.size() }?.size() == 2 - } - } - - /** - * @deprecated Use new mechanism from org.openkilda.functionaltests.helpers.model.SwitchPairs class - */ - @Deprecated - List getAllNotNeighboringSwitchPairs() { - getSwitchPairs().findAll { - it.paths.min { it.size() }?.size() > 2 - } - } - - def traffgenEnabled = { SwitchPair swPair -> - def tgSwitches = topology.activeTraffGens*.switchConnected - swPair.src in tgSwitches && swPair.dst in tgSwitches - } - - /** - * @deprecated Use new mechanism from org.openkilda.functionaltests.helpers.model.SwitchPairs class - */ - @Deprecated - List getSwitchPairs(boolean includeReverse = false) { - //get deep copy - def result = getSwitchPairsCached().collect() - return includeReverse ? result.collectMany { [it, it.reversed] } : result - } - List getSwitchTriplets(boolean includeReverse = false, boolean includeSingleSwitch = false) { //get deep copy def mapper = new ObjectMapper() @@ -231,19 +188,7 @@ class TopologyHelper { }.findAll().unique() } - @Memoized - private List getSwitchPairsCached() { - return [topology.activeSwitches, topology.activeSwitches].combinations() - .findAll { src, dst -> src != dst } //non-single-switch - .unique { it.sort() } //no reversed versions of same flows - .collect { Switch src, Switch dst -> - new SwitchPair(src: src, - dst: dst, - paths: getDbPathsCached(src.dpId, dst.dpId), - northboundService: northbound, - topologyDefinition: topology) - } - } + @Memoized private List getSwitchTripletsCached(boolean includeSingleSwitch) { diff --git a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/model/SwitchPair.groovy b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/model/SwitchPair.groovy index a37f41dc731..d95ae673b17 100644 --- a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/model/SwitchPair.groovy +++ b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/model/SwitchPair.groovy @@ -55,6 +55,15 @@ class SwitchPair { topologyDefinition: topologyDefinition) } + boolean hasOf13Path() { + def possibleDefaultPaths = paths.findAll { + it.size() == paths.min { it.size() }.size() + } + !possibleDefaultPaths.find { path -> + path[1..-2].every { it.switchId.description.contains("OF_12") } + } + } + @Override String toString() { return "$src.dpId-$dst.dpId" diff --git a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/model/SwitchPairs.groovy b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/model/SwitchPairs.groovy index 1aaac3b5c44..964f404de54 100644 --- a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/model/SwitchPairs.groovy +++ b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/helpers/model/SwitchPairs.groovy @@ -1,8 +1,13 @@ package org.openkilda.functionaltests.helpers.model +import groovy.transform.Memoized import org.openkilda.functionaltests.helpers.SwitchHelper import org.openkilda.functionaltests.helpers.TopologyHelper +import org.openkilda.functionaltests.model.switches.Manufacturer +import org.openkilda.testing.model.topology.TopologyDefinition +import org.openkilda.testing.service.northbound.NorthboundService 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 @@ -30,18 +35,24 @@ class SwitchPairs { SwitchHelper switchHelper @Autowired TopologyHelper topologyHelper + @Autowired + TopologyDefinition topology + @Autowired + @Qualifier("islandNb") + NorthboundService northbound SwitchPairs(List switchPairs) { this.switchPairs = switchPairs } SwitchPairs all(Boolean includeReverse = true) { - switchPairs = topologyHelper.getAllSwitchPairs(includeReverse) + def allPairs = getSwitchPairsCached().collect() + switchPairs = includeReverse ? allPairs.collectMany { [it, it.reversed] } : allPairs return this } SwitchPairs singleSwitch() { - switchPairs = topologyHelper.getAllSingleSwitchPairs() + switchPairs = topology.activeSwitches.collect { SwitchPair.singleSwitchInstance(it) } return this } @@ -52,11 +63,28 @@ class SwitchPairs { return this } + SwitchPairs withExactlyNNonOverlappingPaths(int nonOverlappingPaths) { + switchPairs = switchPairs.findAll { + it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() == nonOverlappingPaths + } + return this + } + SwitchPairs withShortestPathShorterThanOthers() { switchPairs = switchPairs.findAll { it.getPaths()[0].size() != it.getPaths()[1].size() } return this } + SwitchPairs withAtLeastNPaths(int minimumPathsAmount) { + switchPairs = switchPairs.findAll { it.getPaths().size() > minimumPathsAmount } + return this + } + + SwitchPairs withTraffgensOnBothEnds() { + switchPairs = switchPairs.findAll { [it.src, it.dst].every { !it.traffGens.isEmpty() } } + return this + } + SwitchPairs nonNeighbouring() { switchPairs = switchPairs.findAll { it.paths.min { it.size() }?.size() > 2 } return this @@ -92,8 +120,18 @@ class SwitchPairs { return this } + SwitchPairs includeSourceSwitch(Switch sw) { + switchPairs = switchPairs.findAll { it.src == sw } + return this + } + SwitchPairs excludeSwitches(List switchesList) { - switchPairs = switchPairs.findAll { !(it.src in switchesList) || !(it.dst in switchesList) } + switchPairs = switchPairs.findAll { !(it.src in switchesList) && !(it.dst in switchesList) } + return this + } + + SwitchPairs excludeDestinationSwitches(List switchesList) { + switchPairs = switchPairs.findAll { it.dst !in switchesList } return this } @@ -106,11 +144,48 @@ class SwitchPairs { return this } + SwitchPairs withAtLeastNTraffgensOnDestination(int traffgensConnectedToDestination) { + switchPairs = switchPairs.findAll { it.getDst().getTraffGens().size() >= traffgensConnectedToDestination } + return this + } + + SwitchPairs withPathHavingAtLeastNSwitches(int minimumSwitchesAmountInAnyPath) { + switchPairs = switchPairs.findAll { + it.paths.find { + it.unique(false) { it.switchId }.size() >= minimumSwitchesAmountInAnyPath + } + } + return this + } + SwitchPairs withBothSwitchesVxLanEnabled() { switchPairs = switchPairs.findAll { [it.src, it.dst].every { sw -> switchHelper.isVxlanEnabled(sw.dpId) } } return this } + SwitchPairs withSwitchesManufacturedBy(Manufacturer srcManufacturer, Manufacturer dstManufacturer) { + switchPairs = switchPairs.findAll { + srcManufacturer.isSwitchMatch(it.getSrc()) + && dstManufacturer.isSwitchMatch(it.getDst()) + && it.hasOf13Path() + } + return this + } + + SwitchPairs withSourceSwitchManufacturedBy(Manufacturer srcManufacturer) { + switchPairs = switchPairs.findAll { + srcManufacturer.isSwitchMatch(it.getSrc()) && it.hasOf13Path() + } + return this + } + + SwitchPairs withSourceSwitchNotManufacturedBy(Manufacturer srcManufacturer) { + switchPairs = switchPairs.findAll { + !srcManufacturer.isSwitchMatch(it.getSrc()) && it.hasOf13Path() + } + return this + } + SwitchPairs withIslRttSupport() { this.assertAllSwitchPairsAreNeighbouring() switchPairs = switchPairs.findAll { [it.src, it.dst].every { it.features.contains(NOVIFLOW_COPY_FIELD) } } @@ -123,12 +198,38 @@ class SwitchPairs { return this } - SwitchPairs withMoreThanNIslsBetweenSwitches(int expectedMinimalIslsBetweenSwitches) { + SwitchPairs withAtLeastNIslsBetweenNeighbouringSwitches(int expectedMinimalIslsBetweenSwitches) { this.assertAllSwitchPairsAreNeighbouring() switchPairs = switchPairs.findAll { - it.paths.findAll { it.size() == 2 } - .size() > expectedMinimalIslsBetweenSwitches - } + it.paths.findAll { it.size() == 2 } + .size() >= expectedMinimalIslsBetweenSwitches + } + return this + } + + SwitchPairs withDestinationSwitchConnectedToServer42() { + switchPairs = switchPairs.findAll { it.dst in topology.getActiveServer42Switches() } + return this + } + + SwitchPairs withOnlySourceSwitchConnectedToServer42() { + switchPairs = switchPairs.findAll { + it.src in topology.getActiveServer42Switches() && !(it.dst in topology.getActiveServer42Switches()) + } + return this + } + + SwitchPairs withBothSwitchesConnectedToServer42() { + switchPairs = switchPairs.findAll { + [it.dst, it.src].every { it in topology.getActiveServer42Switches() } + } + return this + } + + SwitchPairs withBothSwitchesConnectedToSameServer42Instance() { + switchPairs = switchPairs.findAll { + it.src.prop?.server42MacAddress != null && it.src.prop?.server42MacAddress == it.dst.prop?.server42MacAddress + } return this } @@ -136,4 +237,20 @@ class SwitchPairs { assert switchPairs.size() == this.neighbouring().getSwitchPairs().size(), "This method is applicable only to the neighbouring switch pairs" } + + @Memoized + private List getSwitchPairsCached() { + return [topology.activeSwitches, topology.activeSwitches].combinations() + .findAll { src, dst -> src != dst } //non-single-switch + .unique { it.sort() } //no reversed versions of same flows + .collect { Switch src, Switch dst -> + new SwitchPair(src: src, + dst: dst, + paths: topologyHelper.getDbPathsCached(src.dpId, dst.dpId), + northboundService: northbound, + topologyDefinition: topology) + } + } + + } diff --git a/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/model/switches/Manufacturer.groovy b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/model/switches/Manufacturer.groovy new file mode 100644 index 00000000000..cfe07aec6e4 --- /dev/null +++ b/src-java/testing/functional-tests/src/main/groovy/org/openkilda/functionaltests/model/switches/Manufacturer.groovy @@ -0,0 +1,21 @@ +package org.openkilda.functionaltests.model.switches + +import org.openkilda.testing.model.topology.TopologyDefinition + +enum Manufacturer { + OVS("nicira"), + CENTEC("centec"), + NOVIFLOW("noviflow"), + WB5164("WB5164") + + final String descriptionPart + + Manufacturer(String descriptionPart) { + this.descriptionPart = descriptionPart + } + + boolean isSwitchMatch(TopologyDefinition.Switch aSwitch) { + return aSwitch.nbFormat().hardware =~ descriptionPart || + aSwitch.nbFormat().manufacturer.toLowerCase().contains(descriptionPart) + } +} diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/BaseSpecification.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/BaseSpecification.groovy index 7251981637f..d68ffd2f25f 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/BaseSpecification.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/BaseSpecification.groovy @@ -70,8 +70,6 @@ class BaseSpecification extends Specification { @Autowired @Shared StatsHelper statsHelper @Autowired @Shared - LabService labService - @Autowired @Shared SwitchPairs switchPairs @Value('${spring.profiles.active}') @Shared diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/HealthCheckSpecification.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/HealthCheckSpecification.groovy index 370abf1ecd3..1d5b0ab4245 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/HealthCheckSpecification.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/HealthCheckSpecification.groovy @@ -3,15 +3,9 @@ package org.openkilda.functionaltests import static groovyx.gpars.GParsExecutorsPool.withPool import static org.openkilda.bluegreen.Signal.SHUTDOWN import static org.openkilda.bluegreen.Signal.START -import static org.openkilda.model.MeterId.MAX_SYSTEM_RULE_METER_ID -import static org.openkilda.testing.Constants.WAIT_OFFSET import org.openkilda.bluegreen.Signal -import org.openkilda.functionaltests.exception.IslNotFoundException import org.openkilda.functionaltests.helpers.Wrappers -import org.openkilda.messaging.info.event.IslChangeType -import org.openkilda.model.SwitchFeature -import org.openkilda.testing.model.topology.TopologyDefinition.Status import org.openkilda.testing.tools.SoftAssertions import groovy.util.logging.Slf4j @@ -53,71 +47,6 @@ class HealthCheckSpecification extends HealthCheckBaseSpecification { zkAssertions.verify() } - Closure allSwitchesAreActive = { - assert northbound.activeSwitches.size() == topology.switches.findAll { it.status != Status.Inactive }.size() - } - - Closure allLinksAreActive = { - def links = northbound.getAllLinks() - assert links.findAll { it.state != IslChangeType.DISCOVERED }.empty - - def topoLinks = topology.islsForActiveSwitches.collectMany { isl -> - [islUtils.getIslInfo(links, isl).orElseThrow { new IslNotFoundException(isl.toString()) }, - islUtils.getIslInfo(links, isl.reversed).orElseThrow { - new IslNotFoundException(isl.reversed.toString()) - }] - } - def missingLinks = links.findAll { it.state == IslChangeType.DISCOVERED } - topoLinks - assert missingLinks.empty, "These links are missing in topology.yaml" - } - - Closure noFlowsLeft = { - assert northboundV2.allFlows.empty, "There are flows left from previous tests" - } - - Closure noLinkPropertiesLeft = { - assert northbound.allLinkProps.empty - } - - Closure linksBandwidthAndSpeedMatch = { - def speedBwAssertions = new SoftAssertions() - def links = northbound.getAllLinks() - speedBwAssertions.checkSucceeds { assert links.findAll { it.availableBandwidth != it.speed }.empty } - speedBwAssertions.verify() - } - - Closure noExcessRulesMeters = { - def excessRulesAssertions = new SoftAssertions() - withPool { - topology.activeSwitches.eachParallel { sw -> - def rules = northbound.validateSwitchRules(sw.dpId) - excessRulesAssertions.checkSucceeds { assert rules.excessRules.empty, sw } - excessRulesAssertions.checkSucceeds { assert rules.missingRules.empty, sw } - if (!sw.virtual && sw.ofVersion != "OF_12") { - excessRulesAssertions.checkSucceeds { - assert northbound.getAllMeters(sw.dpId).meterEntries.findAll { - it.meterId > MAX_SYSTEM_RULE_METER_ID - }.isEmpty(), "Switch has meters above system max ones" - } - } - } - excessRulesAssertions.verify() - } - } - - Closure allSwitchesConnectedToExpectedRegion = { - def regionVerifications = new SoftAssertions() - flHelper.fls.forEach { fl -> - def expectedSwitchIds = topology.activeSwitches.findAll { fl.region in it.regions }*.dpId - if (!expectedSwitchIds.empty) { - regionVerifications.checkSucceeds { - assert fl.floodlightService.switches*.switchId.sort() == expectedSwitchIds.sort() - } - } - } - regionVerifications.verify() - } - Closure featureTogglesInExpectedState = { verifyAll(northbound.getFeatureToggles()) { flowsRerouteOnIslDiscoveryEnabled @@ -135,25 +64,15 @@ class HealthCheckSpecification extends HealthCheckBaseSpecification { } if(enable) { + log.info("Starting basic Health Check before specs execution") Wrappers.wait(WAIT_FOR_LAB_TO_BE_OPERATIONAL) { withPool { [healthCheckEndpoint, allZookeeperNodesInExpectedState, - allSwitchesAreActive, - allLinksAreActive, - noFlowsLeft, - noLinkPropertiesLeft, - linksBandwidthAndSpeedMatch, - noExcessRulesMeters, - allSwitchesConnectedToExpectedRegion, featureTogglesInExpectedState].eachParallel { it() } } } - } else { - withPool { - [healthCheckEndpoint, - allZookeeperNodesInExpectedState].eachParallel { it() } - } + log.info("Basic Health Check before specs execution passed.") } } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/AutoRerouteSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/AutoRerouteSpec.groovy index fd202937ab9..23569e67cf8 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/AutoRerouteSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/AutoRerouteSpec.groovy @@ -3,6 +3,7 @@ package org.openkilda.functionaltests.spec.flows import static org.openkilda.functionaltests.extension.tags.Tag.ISL_RECOVER_ON_FAIL import org.openkilda.functionaltests.error.flow.FlowNotReroutedExpectedError +import org.openkilda.functionaltests.helpers.model.SwitchPairs import static groovyx.gpars.GParsPool.withPool import static org.junit.jupiter.api.Assumptions.assumeTrue @@ -54,7 +55,7 @@ class AutoRerouteSpec extends HealthCheckSpecification { @IterationTag(tags = [TOPOLOGY_DEPENDENT], iterationNameRegex = /vxlan/) def "Flow is rerouted when one of the #description flow ISLs fails"() { given: "A flow with one alternative path at least" - def data = flowData(switchPairs.all().neighbouring().getSwitchPairs(), 1) + def data = flowData(switchPairs.all().neighbouring(), 1) FlowRequestV2 flow = data[0] def allFlowPaths = data[1] flowHelperV2.addFlow(flow) @@ -83,10 +84,10 @@ class AutoRerouteSpec extends HealthCheckSpecification { where: description | flowData - "vlan" | { List switchPairs, Integer minAltPathsCount -> + "vlan" | { SwitchPairs switchPairs, Integer minAltPathsCount -> getFlowWithPaths(switchPairs, minAltPathsCount) } - "vxlan" | { List switchPairs, Integer minAltPathsCount -> + "vxlan" | { SwitchPairs switchPairs, Integer minAltPathsCount -> getVxlanFlowWithPaths(switchPairs, minAltPathsCount) } } @@ -614,7 +615,7 @@ class AutoRerouteSpec extends HealthCheckSpecification { List mainPath, backupPath, thirdPath List mainIsls, backupIsls Isl mainPathUniqueIsl, commonIsl - def swPair = topologyHelper.switchPairs.find { pair -> + def swPair = switchPairs.all().getSwitchPairs().find { pair -> //we are looking for 2 paths that have a common isl. This ISL should not be used in third path mainPath = pair.paths.find { path -> mainIsls = pathHelper.getInvolvedIsls(path) @@ -717,28 +718,23 @@ triggering one more reroute of the current path" } def noIntermediateSwitchFlow(int minAltPathsCount = 0, boolean getAllPaths = false) { - def flowWithPaths = getFlowWithPaths(switchPairs.all().neighbouring().getSwitchPairs(), + def flowWithPaths = getFlowWithPaths(switchPairs.all().neighbouring(), minAltPathsCount) return getAllPaths ? flowWithPaths : flowWithPaths[0] } def intermediateSwitchFlow(int minAltPathsCount = 0, boolean getAllPaths = false) { - def flowWithPaths = getFlowWithPaths(topologyHelper.getAllNotNeighboringSwitchPairs(), minAltPathsCount) + def flowWithPaths = getFlowWithPaths(switchPairs.all().nonNeighbouring(), minAltPathsCount) return getAllPaths ? flowWithPaths : flowWithPaths[0] } - def getFlowWithPaths(List switchPairs, int minAltPathsCount) { - def switchPair = switchPairs.find { it.paths.size() > minAltPathsCount } ?: - assumeTrue(false, "No suiting switches found") + def getFlowWithPaths(SwitchPairs switchPairs, int minAltPathsCount) { + def switchPair = switchPairs.withAtLeastNPaths(minAltPathsCount + 1).random() return [flowHelperV2.randomFlow(switchPair), switchPair.paths] } - def getVxlanFlowWithPaths(List switchPairs, int minAltPathsCount) { - def switchPair = switchPairs.find {swP -> - swP.paths.findAll { path -> - pathHelper.getInvolvedSwitches(path).every { switchHelper.isVxlanEnabled(it.dpId) } - }.size() > minAltPathsCount - } ?: assumeTrue(false, "No suiting switches found") + def getVxlanFlowWithPaths(SwitchPairs switchPairs, int minAltPathsCount) { + def switchPair = switchPairs.withBothSwitchesVxLanEnabled().withAtLeastNPaths(minAltPathsCount + 1).random() return [flowHelperV2.randomFlow(switchPair), switchPair.paths] } @@ -760,14 +756,14 @@ class AutoRerouteIsolatedSpec extends HealthCheckSpecification { assumeTrue(rerouteDelay * 2 < discoveryTimeout, "Reroute should be completed before link is FAILED") def switchPair1 = switchPairs.all() .neighbouring() - .withMoreThanNIslsBetweenSwitches(1) + .withAtLeastNIslsBetweenNeighbouringSwitches(2) .random() // disable auto-reroute on islDiscovery event northbound.toggleFeature(FeatureTogglesDto.builder().flowsRerouteOnIslDiscoveryEnabled(false).build()) and: "Second switch pair where the srс switch from the first switch pair is a transit switch" List secondFlowPath - def switchPair2 = topologyHelper.switchPairs.collectMany{ [it, it.reversed] }.find { swP -> + def switchPair2 = switchPairs.all().getSwitchPairs().find { swP -> swP.paths.find { pathCandidate -> secondFlowPath = pathCandidate def involvedSwitches = pathHelper.getInvolvedSwitches(pathCandidate) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/BandwidthSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/BandwidthSpec.groovy index 91989c20e3a..02714905bfb 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/BandwidthSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/BandwidthSpec.groovy @@ -2,7 +2,6 @@ package org.openkilda.functionaltests.spec.flows import org.openkilda.functionaltests.error.flow.FlowNotUpdatedWithMissingPathExpectedError -import static org.junit.jupiter.api.Assumptions.assumeTrue import static org.openkilda.functionaltests.extension.tags.Tag.LOW_PRIORITY import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.testing.Constants.WAIT_OFFSET @@ -71,8 +70,7 @@ class BandwidthSpec extends HealthCheckSpecification { def "Longer path is chosen in case of not enough available bandwidth on a shorter path"() { given: "Two active switches with two possible flow paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() // Make the first path more preferable than others. switchPair.paths[1..-1].each { pathHelper.makePathMorePreferable(switchPair.paths.first(), it) } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ConnectedDevicesSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ConnectedDevicesSpec.groovy index 0315e08cc36..85d5b2930e5 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ConnectedDevicesSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ConnectedDevicesSpec.groovy @@ -936,13 +936,7 @@ srcDevices=#newSrcEnabled, dstDevices=#newDstEnabled"() { "Devices+VXLAN problem https://github.com/telstra/open-kilda/issues/3199") given: "Two switches connected to traffgen" - def allTraffGenSwitches = topology.activeTraffGens*.switchConnected - assumeTrue((allTraffGenSwitches.size() > 1), "Unable to find two active traffgens") - def swP = topologyHelper.getAllNeighboringSwitchPairs().find { - [it.src, it.dst].every { sw -> - sw.dpId in allTraffGenSwitches*.dpId && sw.features.contains(SwitchFeature.MULTI_TABLE) - } - } ?: assumeTrue(false, "No suiting switches found") + def swP = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random() and: "A QinQ flow with enabled connected devices" def tgService = traffExamProvider.get() @@ -1028,13 +1022,7 @@ srcDevices=#newSrcEnabled, dstDevices=#newDstEnabled"() { def "System doesn't detect devices only if vlan match with outerVlan of qinq flow"() { given: "Two switches connected to traffgen" - def allTraffGenSwitches = topology.activeTraffGens*.switchConnected - assumeTrue((allTraffGenSwitches.size() > 1), "Unable to find two active traffgens") - def swP = topologyHelper.getAllNeighboringSwitchPairs().find { - [it.src, it.dst].every { sw -> - sw.dpId in allTraffGenSwitches*.dpId && sw.features.contains(SwitchFeature.MULTI_TABLE) - } - } ?: assumeTrue(false, "No suiting switches found") + def swP = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random() and: "A QinQ flow with enabled connected devices" def tgService = traffExamProvider.get() @@ -1419,10 +1407,7 @@ srcDevices=#newSrcEnabled, dstDevices=#newDstEnabled"() { def tgSwitches = topology.activeTraffGens*.switchConnected .findAll { it.features.contains(SwitchFeature.MULTI_TABLE) } def unpickedTgSwitches = tgSwitches.unique(false) { [it.description, it.nbFormat().hardware].sort() } - List switchPairs = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.findAll { - it.src in tgSwitches && it.dst in tgSwitches - } - assumeTrue(switchPairs.size() > 0, "Unable to find a switchPair with traffgens on both sides") + List switchPairs = switchPairs.all().withTraffgensOnBothEnds().getSwitchPairs() def result = [] while (!unpickedTgSwitches.empty) { def pair = switchPairs.sort(false) { switchPair -> diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowAffinitySpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowAffinitySpec.groovy index 72783fd1ce9..f11a7f98c64 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowAffinitySpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowAffinitySpec.groovy @@ -20,7 +20,7 @@ class FlowAffinitySpec extends HealthCheckSpecification { def "Can create more than 2 affinity flows"() { when: "Create flow1" - def swPair = topologyHelper.getSwitchPairs()[0] + def swPair = switchPairs.all().random() def flow1 = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow1) @@ -76,9 +76,9 @@ class FlowAffinitySpec extends HealthCheckSpecification { cost-wise. leastUncommonPaths2 are paths on swPair2 that have the least uncommon ISLs with path1 (but not guaranteed to have any common ones); one of these paths should be chosen regardless of cost-optimal uncommonPath2 when creating affinity flow */ - topologyHelper.getSwitchPairs().find { swP1Candidate -> + switchPairs.all().getSwitchPairs().find{ swP1Candidate -> swPair1 = swP1Candidate - swPair2 = (topologyHelper.getSwitchPairs(true) - swP1Candidate).find { swP2Candidate -> + swPair2 = (switchPairs.all().getSwitchPairs() - swP1Candidate).find { swP2Candidate -> if (swP1Candidate.dst.dpId != swP2Candidate.dst.dpId) return false path1 = swP1Candidate.paths.find { path1Candidate -> List, Integer>> scoreList = [] @@ -124,15 +124,8 @@ class FlowAffinitySpec extends HealthCheckSpecification { def "Affinity flows can have no overlapping switches at all"() { given: "Two switch pairs with not overlapping short paths available" - SwitchPair swPair1, swPair2 - topologyHelper.getAllNeighboringSwitchPairs().find { swP1Candidate -> - swPair1 = swP1Candidate - swPair2 = topologyHelper.getAllNeighboringSwitchPairs().find { swP2Candidate -> - [swP2Candidate.src.dpId, swP2Candidate.dst.dpId].every { - it !in [swP1Candidate.src.dpId, swP1Candidate.dst.dpId] - } - } - } ?: assumeTrue(false, "No suiting switches found") + def swPair1 = switchPairs.all().neighbouring().random() + def swPair2 = switchPairs.all().neighbouring().excludeSwitches([swPair1.src, swPair1.dst]).random() and: "First flow" def flow = flowHelperV2.randomFlow(swPair1) @@ -152,9 +145,7 @@ class FlowAffinitySpec extends HealthCheckSpecification { def "Affinity flow on the same endpoints #willOrNot take the same path if main path cost #exceedsOrNot affinity penalty"() { given: "A neighboring switch pair with parallel ISLs" - def swPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.findAll { it.size() == 2 }.size() > 1 - } ?: assumeTrue(false, "Need a pair of parallel ISLs for this test") + def swPair = switchPairs.all().neighbouring().withAtLeastNIslsBetweenNeighbouringSwitches(2).random() and: "First flow" def flow = flowHelperV2.randomFlow(swPair) @@ -187,7 +178,7 @@ class FlowAffinitySpec extends HealthCheckSpecification { def "Cannot create affinity flow if target flow has affinity with diverse flow"() { given: "Existing flows with diversity" - def swPair = topologyHelper.getSwitchPairs()[0] + def swPair = switchPairs.all().random() def flow1 = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow1) def affinityFlow = flowHelperV2.randomFlow(swPair, false, [flow1]).tap { affinityFlowId = flow1.flowId } @@ -218,7 +209,7 @@ class FlowAffinitySpec extends HealthCheckSpecification { def "Cannot create affinity flow if target flow has another diverse group"() { given: "Existing flows with diversity" - def swPair = topologyHelper.getSwitchPairs()[0] + def swPair = switchPairs.all().random() def flow1 = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow1) def affinityFlow = flowHelperV2.randomFlow(swPair, false, [flow1]).tap { affinityFlowId = flow1.flowId } @@ -248,7 +239,7 @@ class FlowAffinitySpec extends HealthCheckSpecification { flowHelperV2.addFlow(oneSwitchFlow) when: "Create an affinity flow targeting the one-switch flow" - def swPair = topologyHelper.getSwitchPairs(true).find { it.src.dpId == sw.dpId } + def swPair = switchPairs.all().includeSourceSwitch(sw).random() def affinityFlow = flowHelperV2.randomFlow(swPair, false, [oneSwitchFlow]).tap { affinityFlowId = oneSwitchFlow.flowId } flowHelperV2.addFlow(affinityFlow) @@ -271,7 +262,7 @@ class FlowAffinitySpec extends HealthCheckSpecification { def "Error is returned if affinity_with references a non existing flow"() { when: "Create an affinity flow that targets non-existing flow" - def swPair = topologyHelper.getSwitchPairs()[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair).tap { diverseFlowId = NON_EXISTENT_FLOW_ID } flowHelperV2.addFlow(flow) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudSpec.groovy index 1a30b23d2c6..bcdad087dfe 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudSpec.groovy @@ -1,63 +1,39 @@ package org.openkilda.functionaltests.spec.flows -import static org.openkilda.functionaltests.extension.tags.Tag.ISL_RECOVER_ON_FAIL - +import groovy.util.logging.Slf4j +import org.openkilda.functionaltests.HealthCheckSpecification import org.openkilda.functionaltests.error.flow.FlowNotCreatedExpectedError import org.openkilda.functionaltests.error.flow.FlowNotCreatedWithConflictExpectedError import org.openkilda.functionaltests.error.flow.FlowNotCreatedWithMissingPathExpectedError import org.openkilda.functionaltests.error.flow.FlowNotDeletedExpectedError import org.openkilda.functionaltests.error.flow.FlowNotUpdatedExpectedError import org.openkilda.functionaltests.error.flow.FlowNotUpdatedWithMissingPathExpectedError -import org.openkilda.functionaltests.helpers.Wrappers -import org.openkilda.model.PathComputationStrategy -import org.openkilda.northbound.dto.v2.flows.FlowPatchEndpoint -import org.openkilda.northbound.dto.v2.flows.FlowPatchV2 -import org.openkilda.northbound.dto.v2.flows.FlowResponseV2 -import org.openkilda.northbound.dto.v2.flows.FlowStatistics - -import static groovyx.gpars.GParsPool.withPool -import static org.junit.jupiter.api.Assumptions.assumeTrue -import static org.openkilda.functionaltests.extension.tags.Tag.LOW_PRIORITY -import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE -import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE_SWITCHES -import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT -import static org.openkilda.functionaltests.helpers.Wrappers.wait -import static org.openkilda.functionaltests.helpers.Wrappers.timedLoop -import static org.openkilda.messaging.info.event.IslChangeType.DISCOVERED -import static org.openkilda.messaging.info.event.IslChangeType.FAILED -import static org.openkilda.messaging.info.event.IslChangeType.MOVED -import static org.openkilda.messaging.payload.flow.FlowState.IN_PROGRESS -import static org.openkilda.messaging.payload.flow.FlowState.UP -import static org.openkilda.testing.Constants.PATH_INSTALLATION_TIME -import static org.openkilda.testing.Constants.RULES_DELETION_TIME -import static org.openkilda.testing.Constants.RULES_INSTALLATION_TIME -import static org.openkilda.testing.Constants.WAIT_OFFSET -import static org.openkilda.testing.service.floodlight.model.FloodlightConnectMode.RW - -import org.openkilda.functionaltests.HealthCheckSpecification import org.openkilda.functionaltests.extension.tags.IterationTag import org.openkilda.functionaltests.extension.tags.IterationTags import org.openkilda.functionaltests.extension.tags.Tags import org.openkilda.functionaltests.helpers.PathHelper +import org.openkilda.functionaltests.helpers.Wrappers import org.openkilda.functionaltests.helpers.model.SwitchPair import org.openkilda.messaging.info.event.PathNode import org.openkilda.messaging.payload.flow.DetectConnectedDevicesPayload import org.openkilda.messaging.payload.flow.FlowEndpointPayload import org.openkilda.messaging.payload.flow.FlowPayload -import org.openkilda.model.FlowEncapsulationType +import org.openkilda.model.PathComputationStrategy import org.openkilda.model.SwitchId import org.openkilda.model.cookie.Cookie import org.openkilda.model.cookie.CookieBase.CookieType import org.openkilda.northbound.dto.v1.flows.PingInput import org.openkilda.northbound.dto.v2.flows.FlowEndpointV2 +import org.openkilda.northbound.dto.v2.flows.FlowPatchEndpoint +import org.openkilda.northbound.dto.v2.flows.FlowPatchV2 import org.openkilda.northbound.dto.v2.flows.FlowRequestV2 +import org.openkilda.northbound.dto.v2.flows.FlowResponseV2 +import org.openkilda.northbound.dto.v2.flows.FlowStatistics import org.openkilda.testing.model.topology.TopologyDefinition.Isl import org.openkilda.testing.model.topology.TopologyDefinition.Switch import org.openkilda.testing.service.traffexam.FlowNotApplicableException import org.openkilda.testing.service.traffexam.TraffExamService import org.openkilda.testing.tools.FlowTrafficExamBuilder - -import groovy.util.logging.Slf4j import org.springframework.beans.factory.annotation.Autowired import org.springframework.web.client.HttpClientErrorException import spock.lang.Narrative @@ -67,6 +43,28 @@ import spock.lang.Unroll import javax.inject.Provider +import static groovyx.gpars.GParsPool.withPool +import static org.junit.jupiter.api.Assumptions.assumeTrue +import static org.openkilda.functionaltests.extension.tags.Tag.ISL_RECOVER_ON_FAIL +import static org.openkilda.functionaltests.extension.tags.Tag.LOW_PRIORITY +import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE +import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE_SWITCHES +import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT +import static org.openkilda.functionaltests.helpers.Wrappers.timedLoop +import static org.openkilda.functionaltests.helpers.Wrappers.wait +import static org.openkilda.messaging.info.event.IslChangeType.DISCOVERED +import static org.openkilda.messaging.info.event.IslChangeType.FAILED +import static org.openkilda.messaging.info.event.IslChangeType.MOVED +import static org.openkilda.messaging.payload.flow.FlowState.IN_PROGRESS +import static org.openkilda.messaging.payload.flow.FlowState.UP +import static org.openkilda.model.FlowEncapsulationType.TRANSIT_VLAN +import static org.openkilda.model.FlowEncapsulationType.VXLAN +import static org.openkilda.testing.Constants.PATH_INSTALLATION_TIME +import static org.openkilda.testing.Constants.RULES_DELETION_TIME +import static org.openkilda.testing.Constants.RULES_INSTALLATION_TIME +import static org.openkilda.testing.Constants.WAIT_OFFSET +import static org.openkilda.testing.service.floodlight.model.FloodlightConnectMode.RW + @Slf4j @See("https://github.com/telstra/open-kilda/tree/develop/docs/design/hub-and-spoke/crud") @Narrative(""""Verify CRUD operations and health of basic vlan flows on different types of switches. @@ -505,19 +503,20 @@ class FlowCrudSpec extends HealthCheckSpecification { [ isolatedSwitchType: "source", getFlow : { Switch theSwitch -> - getFlowHelperV2().randomFlow(getTopologyHelper().getAllNotNeighboringSwitchPairs() - .collectMany { [it, it.reversed] }.find { - it.src == theSwitch - }) + getFlowHelperV2().randomFlow(switchPairs.all() + .nonNeighbouring() + .includeSourceSwitch(theSwitch) + .random()) } ], [ isolatedSwitchType: "destination", getFlow : { Switch theSwitch -> - getFlowHelperV2().randomFlow(getTopologyHelper().getAllNotNeighboringSwitchPairs() - .collectMany { [it, it.reversed] }.find { - it.dst == theSwitch - }) + getFlowHelperV2().randomFlow(switchPairs.all() + .nonNeighbouring() + .includeSourceSwitch(theSwitch) + .random() + .getReversed()) } ] ] @@ -778,9 +777,7 @@ Failed to find path with requested bandwidth=${IMPOSSIBLY_HIGH_BANDWIDTH}/) def "System allows to CRUD protected flow"() { given: "Two active not neighboring switches with two diverse paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() when: "Create flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -880,42 +877,37 @@ Failed to find path with requested bandwidth=${IMPOSSIBLY_HIGH_BANDWIDTH}/) def "System doesn't ignore encapsulationType when flow is created with ignoreBandwidth = true"() { given: "Two active switches" - def swPair = switchPairs.all().neighbouring().getSwitchPairs().find { - [it.src, it.dst].any { !switchHelper.isVxlanEnabled(it.dpId) } - } ?: assumeTrue(false, "Unable to find required switches in topology") + def swPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().random() - def srcProps = switchHelper.getCachedSwProps(swPair.src.dpId) - def dstProps = switchHelper.getCachedSwProps(swPair.dst.dpId) - def endpointName = "source" + def initialSrcProps = switchHelper.getCachedSwProps(swPair.src.dpId) + def initialSupportedEncapsulations = initialSrcProps.getSupportedTransitEncapsulation().collect() + switchHelper.updateSwitchProperties(swPair.getSrc(), initialSrcProps.tap { + it.supportedTransitEncapsulation = [TRANSIT_VLAN.toString()]}) - def swWithoutVxlan = swPair.src - def encapsTypesWithoutVxlan = srcProps.supportedTransitEncapsulation.collect { it.toString().toUpperCase() } - - if (srcProps.supportedTransitEncapsulation.contains(FlowEncapsulationType.VXLAN.toString().toLowerCase())) { - swWithoutVxlan = swPair.dst - encapsTypesWithoutVxlan = dstProps.supportedTransitEncapsulation.collect { it.toString().toUpperCase() } - endpointName = "destination" - } when: "Create a flow with not supported encapsulation type on the switches" def flow = flowHelperV2.randomFlow(swPair) flow.ignoreBandwidth = true flow.maximumBandwidth = 0 - flow.encapsulationType = FlowEncapsulationType.VXLAN + flow.encapsulationType = VXLAN flowHelperV2.addFlow(flow) then: "Human readable error is returned" def exc = thrown(HttpClientErrorException) - new FlowNotCreatedExpectedError(~/Flow\'s $endpointName endpoint $swWithoutVxlan.dpId doesn\'t support \ -requested encapsulation type $FlowEncapsulationType.VXLAN. Choose one of the supported encapsulation \ + new FlowNotCreatedExpectedError(~/Flow\'s source endpoint ${swPair.getSrc().getDpId()} doesn\'t support \ +requested encapsulation type $VXLAN. Choose one of the supported encapsulation \ types .* or update switch properties and add needed encapsulation type./).matches(exc) + cleanup: !exc && flowHelperV2.deleteFlow(flow.flowId) + initialSrcProps && switchHelper.updateSwitchProperties(swPair.getSrc(), initialSrcProps.tap { + it.supportedTransitEncapsulation = initialSupportedEncapsulations + }) } def "Flow status accurately represents the actual state of the flow and flow rules"() { when: "Create a flow on a long path" - def swPair = topologyHelper.switchPairs.first() + def swPair = switchPairs.all().random() def longPath = swPair.paths.max { it.size() } swPair.paths.findAll { it != longPath }.each { pathHelper.makePathMorePreferable(longPath, it) } def flow = flowHelperV2.randomFlow(swPair) @@ -1092,9 +1084,7 @@ types .* or update switch properties and add needed encapsulation type./).matche @Tags(LOW_PRIORITY) def "System reroutes flow to more preferable path while updating"() { given: "Two active not neighboring switches with two possible paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNPaths(2).random() and: "A flow" def flow = flowHelperV2.randomFlow(switchPair) @@ -1137,13 +1127,7 @@ types .* or update switch properties and add needed encapsulation type./).matche def "System doesn't rebuild path for a flow to more preferable path while updating portNumber/vlanId"() { given: "Two active switches connected to traffgens with two possible paths at least" - def activeTraffGens = topology.activeTraffGens - def allTraffgenSwitches = activeTraffGens*.switchConnected ?: - assumeTrue(false, "Should be at least two active traffgens connected to switches") - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { swP -> - allTraffgenSwitches*.dpId.contains(swP.src.dpId) && allTraffgenSwitches*.dpId.contains(swP.dst.dpId) && - swP.paths.size() >= 2 - } ?: assumeTrue(false, "Unable to find required switches/paths in topology") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() and: "A flow" def flow = flowHelperV2.randomFlow(switchPair, false) @@ -1190,9 +1174,9 @@ types .* or update switch properties and add needed encapsulation type./).matche then: "Update the flow: port number and vlanId on the src/dst endpoints" def updatedFlow = flow.jacksonCopy().tap { - it.source.portNumber = activeTraffGens.find { it.switchConnected.dpId == switchPair.src.dpId }.switchPort + it.source.portNumber = switchPair.getSrc().getTraffGens().first().getSwitchPort() it.source.vlanId = updatedFlowDstEndpoint.source.vlanId - 1 - it.destination.portNumber = activeTraffGens.find { it.switchConnected.dpId == switchPair.dst.dpId }.switchPort + it.destination.portNumber = switchPair.getDst().getTraffGens().first().getSwitchPort() it.destination.vlanId = updatedFlowDstEndpoint.destination.vlanId - 1 } flowHelperV2.updateFlow(flow.flowId, updatedFlow) @@ -1287,7 +1271,7 @@ types .* or update switch properties and add needed encapsulation type./).matche def "Unable to create a flow with both strict_bandwidth and ignore_bandwidth flags"() { when: "Try to create a flow with strict_bandwidth:true and ignore_bandwidth:true" - def flow = flowHelperV2.randomFlow(topologyHelper.switchPairs[0]).tap { + def flow = flowHelperV2.randomFlow(switchPairs.all().random()).tap { strictBandwidth = true ignoreBandwidth = true } @@ -1305,7 +1289,7 @@ types .* or update switch properties and add needed encapsulation type./).matche @Tags([LOW_PRIORITY]) def "Unable to update flow with incorrect id in request body"() { given:"A flow" - def flow = flowHelperV2.randomFlow(topologyHelper.switchPairs[0]) + def flow = flowHelperV2.randomFlow(switchPairs.all().random()) def oldFlowId = flowHelperV2.addFlow(flow).getFlowId() def newFlowId = "new_flow_id" @@ -1326,7 +1310,7 @@ types .* or update switch properties and add needed encapsulation type./).matche @Tags([LOW_PRIORITY]) def "Unable to update flow with incorrect id in request path"() { given: "A flow" - def flow = flowHelperV2.randomFlow(topologyHelper.switchPairs[0]) + def flow = flowHelperV2.randomFlow(switchPairs.all().random()) flowHelperV2.addFlow(flow) def newFlowId = "new_flow_id" @@ -1347,7 +1331,7 @@ types .* or update switch properties and add needed encapsulation type./).matche @Tags(LOW_PRIORITY) def "Able to #method update with empty VLAN stats and non-zero VLANs (#5063)"() { given: "A flow with non empty vlans stats and with src and dst vlans set to '0'" - def switches = topologyHelper.getSwitchPairs().shuffled().first() + def switches = switchPairs.all().random() def flowRequest = flowHelperV2.randomFlow(switches, false).tap { it.source.tap { it.vlanId = 0 } it.destination.tap { it.vlanId = 0 } @@ -1457,7 +1441,7 @@ types .* or update switch properties and add needed encapsulation type./).matche * By unique flows it considers combinations of unique src/dst switch descriptions and OF versions. */ def getFlowsWithoutTransitSwitch() { - def switchPairs = topologyHelper.getAllNeighboringSwitchPairs().sort(traffgensPrioritized) + def switchPairs = switchPairs.all(false).neighbouring().getSwitchPairs().sort(traffgensPrioritized) .unique { [it.src, it.dst]*.description.sort() } return switchPairs.inject([]) { r, switchPair -> @@ -1485,7 +1469,7 @@ types .* or update switch properties and add needed encapsulation type./).matche * By unique flows it considers combinations of unique src/dst switch descriptions and OF versions. */ def getFlowsWithTransitSwitch() { - def switchPairs = topologyHelper.getAllNotNeighboringSwitchPairs().sort(traffgensPrioritized) + def switchPairs = switchPairs.all().nonNeighbouring().getSwitchPairs().sort(traffgensPrioritized) .unique { [it.src, it.dst]*.description.sort() } return switchPairs.inject([]) { r, switchPair -> diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudV1Spec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudV1Spec.groovy index e2bf072fee2..ec75995c467 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudV1Spec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudV1Spec.groovy @@ -535,19 +535,20 @@ Failed to find path with requested bandwidth=$flow.maximumBandwidth/).matches(er [ isolatedSwitchType: "source", getFlow : { Switch theSwitch -> - getFlowHelper().randomFlow(getTopologyHelper().getAllNotNeighboringSwitchPairs() - .collectMany { [it, it.reversed] }.find { - it.src == theSwitch - }) + getFlowHelper().randomFlow(switchPairs.all() + .nonNeighbouring() + .includeSourceSwitch(theSwitch) + .random()) } ], [ isolatedSwitchType: "destination", getFlow : { Switch theSwitch -> - getFlowHelper().randomFlow(getTopologyHelper().getAllNotNeighboringSwitchPairs() - .collectMany { [it, it.reversed] }.find { - it.dst == theSwitch - }) + getFlowHelper().randomFlow(switchPairs.all() + .nonNeighbouring() + .includeSourceSwitch(theSwitch) + .random() + .getReversed()) } ] ] @@ -863,9 +864,7 @@ are not connected to the controller/).matches(exc) @Tags([ISL_RECOVER_ON_FAIL, ISL_PROPS_DB_RESET]) def "System doesn't create flow when reverse path has different bandwidth than forward path on the second link"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.find { it.unique(false) { it.switchId }.size() >= 4 } - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withPathHavingAtLeastNSwitches(4).random() and: "Select path for further manipulation with it" def selectedPath = switchPair.paths.max { it.size() } @@ -954,7 +953,7 @@ are not connected to the controller/).matches(exc) * By unique flows it considers combinations of unique src/dst switch descriptions and OF versions. */ def getFlowsWithoutTransitSwitch() { - def switchPairs = topologyHelper.getAllNeighboringSwitchPairs().sort(traffgensPrioritized) + def switchPairs = switchPairs.all(false).neighbouring().getSwitchPairs().sort(traffgensPrioritized) .unique { [it.src, it.dst]*.description.sort() } return switchPairs.inject([]) { r, switchPair -> @@ -982,7 +981,7 @@ are not connected to the controller/).matches(exc) * By unique flows it considers combinations of unique src/dst switch descriptions and OF versions. */ def getFlowsWithTransitSwitch() { - def switchPairs = topologyHelper.getAllNotNeighboringSwitchPairs().sort(traffgensPrioritized) + def switchPairs = switchPairs.all().nonNeighbouring().getSwitchPairs().sort(traffgensPrioritized) .unique { [it.src, it.dst]*.description.sort() } return switchPairs.inject([]) { r, switchPair -> diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowDiversitySpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowDiversitySpec.groovy index ec682f00544..0158887a555 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowDiversitySpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowDiversitySpec.groovy @@ -1,7 +1,6 @@ package org.openkilda.functionaltests.spec.flows import static groovyx.gpars.GParsExecutorsPool.withPool -import static org.junit.jupiter.api.Assumptions.assumeTrue import static org.openkilda.functionaltests.extension.tags.Tag.ISL_RECOVER_ON_FAIL import static org.openkilda.functionaltests.extension.tags.Tag.LOW_PRIORITY import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE @@ -14,7 +13,6 @@ import org.openkilda.functionaltests.HealthCheckSpecification import org.openkilda.functionaltests.extension.tags.Tags import org.openkilda.functionaltests.helpers.PathHelper import org.openkilda.functionaltests.helpers.Wrappers -import org.openkilda.functionaltests.helpers.model.SwitchPair import org.openkilda.messaging.info.event.IslChangeType import org.openkilda.messaging.info.event.PathNode import org.openkilda.messaging.payload.flow.FlowPathPayload @@ -50,7 +48,7 @@ class FlowDiversitySpec extends HealthCheckSpecification { @Tags(SMOKE) def "Able to create diverse flows"() { given: "Two active neighboring switches with three not overlapping paths at least" - def switchPair = getSwitchPair(3) + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(3).random() when: "Create three flows with diversity enabled" def flow1 = flowHelperV2.randomFlow(switchPair, false) @@ -103,7 +101,7 @@ class FlowDiversitySpec extends HealthCheckSpecification { def "Able to update flows to become diverse"() { given: "Two active neighboring switches with three not overlapping paths at least" - def switchPair = getSwitchPair(3) + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(3).random() and: "Create three flows" def flow1 = flowHelperV2.randomFlow(switchPair, false) @@ -157,7 +155,7 @@ class FlowDiversitySpec extends HealthCheckSpecification { @Tags(SMOKE) def "Able to update flows to become not diverse"() { given: "Two active neighboring switches with three not overlapping paths at least" - def switchPair = getSwitchPair(3) + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(3).random() and: "Create three flows with diversity enabled" def flow1 = flowHelperV2.randomFlow(switchPair, false) @@ -212,7 +210,7 @@ class FlowDiversitySpec extends HealthCheckSpecification { @Tags([SMOKE, ISL_RECOVER_ON_FAIL]) def "Diverse flows are built through the same path if there are no alternative paths available"() { given: "Two active neighboring switches with two not overlapping paths at least" - def switchPair = getSwitchPair(2) + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() and: "Create a flow going through these switches" def flow1 = flowHelperV2.randomFlow(switchPair) @@ -252,7 +250,11 @@ class FlowDiversitySpec extends HealthCheckSpecification { @Tags(SMOKE) def "Links and switches get extra cost that is considered while calculating diverse flow paths"() { given: "Two active neighboring switches with three not overlapping paths at least" - def switchPair = getSwitchPair(3) + def switchPair = switchPairs.all() + .neighbouring() + .withAtLeastNNonOverlappingPaths(3) + .withAtLeastNIslsBetweenNeighbouringSwitches(2) + .random() and: "Create a flow going through these switches" def flow1 = flowHelperV2.randomFlow(switchPair) @@ -300,7 +302,7 @@ class FlowDiversitySpec extends HealthCheckSpecification { def "Able to get flow paths with correct overlapping segments stats (casual flows)"() { given: "Two active neighboring switches with three not overlapping paths at least" - def switchPair = getSwitchPair(3) + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(3).random() and: "Create three flows with diversity enabled" def flow1 = flowHelperV2.randomFlow(switchPair, false) @@ -390,7 +392,7 @@ class FlowDiversitySpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Able to create diverse flows [v1 api]"() { given: "Two active neighboring switches with three not overlapping paths at least" - def switchPair = getSwitchPair(3) + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(3).random() when: "Create three flows with diversity enabled" def flow1 = flowHelper.randomFlow(switchPair, false) @@ -413,13 +415,6 @@ class FlowDiversitySpec extends HealthCheckSpecification { [flow1, flow2, flow3].each { it && flowHelper.deleteFlow(it.id) } } - SwitchPair getSwitchPair(minNotOverlappingPaths) { - topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.collect { pathHelper.getInvolvedIsls(it) }.unique { a, b -> a.intersect(b) ? 0 : 1 }.size() >= - minNotOverlappingPaths - } ?: assumeTrue(false, "No suiting switches found") - } - void verifySegmentsStats(List flowPaths, Map expectedValuesMap) { flowPaths.each { flow -> with(flow.diverseGroupPayload) { diverseGroup -> diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowLoopSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowLoopSpec.groovy index 7ba9f26ff08..e7732490ace 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowLoopSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowLoopSpec.groovy @@ -61,10 +61,7 @@ class FlowLoopSpec extends HealthCheckSpecification { ]) def "Able to create flowLoop for a #data.flowDescription flow"() { given: "An active and valid #data.flowDescription flow" - def allTraffGenSwIds = topology.activeTraffGens*.switchConnected*.dpId - assumeTrue((allTraffGenSwIds.size() > 1), "Unable to find switches connected to traffGens") - def switchPair = data.switchPair(allTraffGenSwIds) - assumeTrue(switchPair != null, "Unable to find required switch pair in topology") + def switchPair = data.switchPair def flow = flowHelperV2.randomFlow(switchPair) flow.tap(data.flowTap) flowHelperV2.addFlow(flow) @@ -194,16 +191,12 @@ class FlowLoopSpec extends HealthCheckSpecification { where: data << [[ flowDescription: "pinned", - switchPair : { List switchIds -> - getSwPairConnectedToTraffGenForSimpleFlow(switchIds) - }, + switchPair : switchPairs.all().withTraffgensOnBothEnds().random(), flowTap : { FlowRequestV2 fl -> fl.pinned = true } ], [ flowDescription: "default", - switchPair : { List switchIds -> - getSwPairConnectedToTraffGenForSimpleFlow(switchIds) - }, + switchPair : switchPairs.all().withTraffgensOnBothEnds().random(), flowTap : { FlowRequestV2 fl -> fl.source.vlanId = 0 fl.destination.vlanId = 0 @@ -211,23 +204,25 @@ class FlowLoopSpec extends HealthCheckSpecification { ], [ flowDescription: "protected", - switchPair : { List switchIds -> - getSwPairConnectedToTraffGenForProtectedFlow(switchIds) - }, + switchPair : switchPairs.all() + .withTraffgensOnBothEnds() + .withAtLeastNNonOverlappingPaths(2) + .random(), flowTap : { FlowRequestV2 fl -> fl.allocateProtectedPath = true } ], [ flowDescription: "vxlan", - switchPair : { List switchIds -> - getSwPairConnectedToTraffGenForVxlanFlow(switchIds) - }, + switchPair : switchPairs.all() + .withTraffgensOnBothEnds() + .withBothSwitchesVxLanEnabled() + .random(), flowTap : { FlowRequestV2 fl -> fl.encapsulationType = FlowEncapsulationType.VXLAN } ], [ flowDescription: "qinq", - switchPair : { List switchIds -> - getSwPairConnectedToTraffGenForQinQ(switchIds) - }, + switchPair : switchPairs.all() + .withTraffgensOnBothEnds() + .random(), flowTap : { FlowRequestV2 fl -> fl.source.vlanId = 10 fl.source.innerVlanId = 100 @@ -241,7 +236,7 @@ class FlowLoopSpec extends HealthCheckSpecification { @Tags(LOW_PRIORITY) def "Able to delete a flow with created flowLoop on it"() { given: "A active multi switch flow" - def switchPair = topologyHelper.switchPairs.first() + def switchPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(switchPair) flowHelperV2.addFlow(flow) @@ -292,11 +287,11 @@ class FlowLoopSpec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "System is able to reroute a flow when flowLoop is created on it"() { given: "A multi switch flow with one alternative path at least" - def allTraffGenSwIds = topology.activeTraffGens*.switchConnected*.dpId - assumeTrue((allTraffGenSwIds.size() > 1), "Unable to find switches connected to traffGens") - // pick swPair for protected flow, we can fail any ISL on a flow path and be sure that an alternative path is available - def switchPair = getSwPairConnectedToTraffGenForProtectedFlow(allTraffGenSwIds) - assumeTrue(switchPair != null, "Unable to find required switch pair in topology") + def switchPair = switchPairs.all() + .nonNeighbouring() + .withTraffgensOnBothEnds() + .withAtLeastNNonOverlappingPaths(2) + .random() def flow = flowHelperV2.randomFlow(switchPair) flowHelperV2.addFlow(flow) def flowPath = PathHelper.convert(northbound.getFlowPath(flow.flowId)) @@ -398,11 +393,7 @@ class FlowLoopSpec extends HealthCheckSpecification { @Tags(LOW_PRIORITY) def "Systems allows to get all flowLoops that goes through a switch"() { given: "Two active switches" - def switchPair = topologyHelper.switchPairs.find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } - assumeTrue(switchPair != null, "Unable to find required switch pair in topology") - + def switchPair = switchPairs.all().withAtLeastNNonOverlappingPaths(2).random() and: "Three multi switch flows" def flow1 = flowHelperV2.randomFlow(switchPair) flow1.allocateProtectedPath = true @@ -448,14 +439,10 @@ class FlowLoopSpec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "System is able to autoSwapPath for a protected flow when flowLoop is created on it"() { given: "Two active switches with three diverse paths at least" - def allTraffGenSwIds = topology.activeTraffGens*.switchConnected*.dpId - assumeTrue((allTraffGenSwIds.size() > 1), "Unable to find switches connected to traffGens") - def switchPair = topologyHelper.getSwitchPairs().find { - [it.dst, it.src].every { it.dpId in allTraffGenSwIds } && it.paths.unique(false) { - a, b -> a.intersect(b) == [] ? 1 : 0 - }.size() >= 3 - } ?: assumeTrue(false, "No suiting switches found") - + def switchPair = switchPairs.all() + .withAtLeastNNonOverlappingPaths(3) + .withTraffgensOnBothEnds() + .random() and: "A protected unmetered flow with flowLoop on the src switch" def flow = flowHelperV2.randomFlow(switchPair).tap { @@ -791,34 +778,6 @@ class FlowLoopSpec extends HealthCheckSpecification { flow && flowHelperV2.deleteFlow(flow.flowId) } - def "getSwPairConnectedToTraffGenForSimpleFlow"(List switchIds) { - getTopologyHelper().getSwitchPairs().collectMany { [it, it.reversed] }.find { swP -> - [swP.dst, swP.src].every { it.dpId in switchIds } - } - } - - def "getSwPairConnectedToTraffGenForProtectedFlow"(List switchIds) { - getTopologyHelper().getSwitchPairs().find { - [it.dst, it.src].every { it.dpId in switchIds } && it.paths.unique(false) { - a, b -> a.intersect(b) == [] ? 1 : 0 - }.size() >= 2 - } - } - - def "getSwPairConnectedToTraffGenForVxlanFlow"(List switchIds) { - getTopologyHelper().getSwitchPairs().find { - [it.dst, it.src].every { - it.dpId in switchIds && switchHelper.isVxlanEnabled(it.dpId) - } - } - } - - def "getSwPairConnectedToTraffGenForQinQ"(List switchIds) { - return getTopologyHelper().getSwitchPairs().find { swP -> - [swP.dst, swP.src].every { it.dpId in switchIds } - } - } - def getFlowLoopRules(SwitchId switchId) { def forwardLoopRule def reverseLoopRule diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowMonitoringSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowMonitoringSpec.groovy index ec6ac6979da..7dc2f8f1d9d 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowMonitoringSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowMonitoringSpec.groovy @@ -58,11 +58,8 @@ class FlowMonitoringSpec extends HealthCheckSpecification { def setupSpec() { //setup: Two active switches with two diverse paths - List> paths - switchPair = topologyHelper.switchPairs.find { - paths = it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 } - paths.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + switchPair = switchPairs.all().withAtLeastNNonOverlappingPaths(2).random() + List> paths = switchPair.getPaths() mainPath = paths[0] alternativePath = paths[1] mainIsls = pathHelper.getInvolvedIsls(mainPath) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowPingSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowPingSpec.groovy index 3cd28637289..deaf5f52919 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowPingSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowPingSpec.groovy @@ -95,12 +95,7 @@ class FlowPingSpec extends HealthCheckSpecification { @Tags([TOPOLOGY_DEPENDENT]) def "Able to ping a flow with vxlan"() { given: "A flow with random vxlan" - //defining switches pair with vxlan support - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { swP -> - [swP.src, swP.dst].every { switchHelper.isVxlanEnabled(it.dpId) } - } - assumeTrue(switchPair as boolean, "Unable to find required switches in topology") - + def switchPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().random() def flow = flowHelperV2.randomFlow(switchPair) flow.encapsulationType = FlowEncapsulationType.VXLAN flowHelperV2.addFlow(flow) @@ -293,13 +288,7 @@ class FlowPingSpec extends HealthCheckSpecification { @Tags(TOPOLOGY_DEPENDENT) def "Flow ping can detect a broken path for a vxlan flow on an intermediate switch"() { given: "A vxlan flow with intermediate switch(es)" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { swP -> - swP.paths.findAll { path -> - pathHelper.getInvolvedSwitches(path).every { - switchHelper.isVxlanEnabled(it.dpId) - } - }.size() >= 1 - } ?: assumeTrue(false, "Unable to find required switches in topology") + def switchPair = switchPairs.all().nonNeighbouring().withBothSwitchesVxLanEnabled().random() def flow = flowHelperV2.randomFlow(switchPair) flow.encapsulationType = FlowEncapsulationType.VXLAN diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowSyncSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowSyncSpec.groovy index e87f20d8500..75e3eb94d4e 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowSyncSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowSyncSpec.groovy @@ -30,7 +30,6 @@ class FlowSyncSpec extends HealthCheckSpecification { def "Able to synchronize a flow (install missing flow rules, reinstall existing) without rerouting"() { given: "An intermediate-switch flow with deleted rules on src switch" def switchPair = switchPairs.all().nonNeighbouring().random() - assumeTrue(switchPair.asBoolean(), "Need a not-neighbouring switch pair for this test") def flow = flowHelperV2.randomFlow(switchPair) flowHelperV2.addFlow(flow) @@ -75,73 +74,6 @@ class FlowSyncSpec extends HealthCheckSpecification { flow && flowHelperV2.deleteFlow(flow.flowId) } - @Ignore("After PR4817 flow sync never change existing paths") - def "Able to synchronize a flow (install missing flow rules, reinstall existing) with rerouting"() { - given: "An intermediate-switch flow with two possible paths at least and deleted rules on src switch" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found to build an intermediate-switch flow " + - "with two possible paths at least.") - - def flow = flowHelperV2.randomFlow(switchPair) - flowHelperV2.addFlow(flow) - def flowPath = PathHelper.convert(northbound.getFlowPath(flow.flowId)) - - def involvedSwitches = pathHelper.getInvolvedSwitches(flow.flowId) - List rulesToDelete = getFlowRules(switchPair.src)*.cookie - rulesToDelete.each { northbound.deleteSwitchRules(switchPair.src.dpId, it) } - Wrappers.wait(RULES_DELETION_TIME) { - assert getFlowRules(switchPair.src).size() == flowRulesCount - rulesToDelete.size() - } - - and: "Make one of the alternative flow paths more preferable than the current one" - switchPair.paths.findAll { it != flowPath }.each { pathHelper.makePathMorePreferable(it, flowPath) } - - when: "Synchronize the flow" - def syncTime = new Date() - def rerouteResponse = northbound.synchronizeFlow(flow.flowId) - Wrappers.wait(WAIT_OFFSET) { assert northboundV2.getFlowStatus(flow.flowId).status == FlowState.UP } - - then: "The flow is rerouted" - def newFlowPath = PathHelper.convert(northbound.getFlowPath(flow.flowId)) - int seqId = 0 - - rerouteResponse.rerouted - rerouteResponse.path.path == newFlowPath - rerouteResponse.path.path.each { assert it.seqId == seqId++ } - - newFlowPath != flowPath - - and: "Flow rules are installed/reinstalled on switches remained from the original flow path" - def involvedSwitchesAfterSync = pathHelper.getInvolvedSwitches(flow.flowId) - involvedSwitchesAfterSync.findAll { it in involvedSwitches }.each { sw -> - Wrappers.wait(RULES_INSTALLATION_TIME) { - def flowRules = getFlowRules(sw) - assert flowRules.size() == flowRulesCount - flowRules.each { - assert it.durationSeconds < TimeCategory.minus(new Date(), syncTime).toMilliseconds() / 1000.0 - } - } - } - - and: "Flow rules are installed on new switches involved in the current flow path" - involvedSwitchesAfterSync.findAll { !(it in involvedSwitches) }.each { sw -> - Wrappers.wait(RULES_INSTALLATION_TIME) { assert getFlowRules(sw).size() == flowRulesCount } - } - - and: "Flow rules are deleted from switches that are NOT involved in the current flow path" - involvedSwitches.findAll { !(it in involvedSwitchesAfterSync) }.each { sw -> - Wrappers.wait(RULES_DELETION_TIME) { assert getFlowRules(sw).empty } - } || true // switches after sync may include all switches involved in the flow before sync - - and: "Flow is valid" - northbound.validateFlow(flow.flowId).each { direction -> assert direction.asExpected } - - cleanup: "Delete the flow and link props, reset link costs" - flow && flowHelperV2.deleteFlow(flow.flowId) - northbound.deleteLinkProps(northbound.getLinkProps(topology.isls)) - database.resetCosts(topology.isls) - } - List getFlowRules(Switch sw) { northbound.getSwitchRules(sw.dpId).flowEntries.findAll { def cookie = new Cookie(it.cookie) cookie.type == CookieType.SERVICE_OR_FLOW_SEGMENT && !cookie.serviceFlag diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowValidationNegativeSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowValidationNegativeSpec.groovy index f10b7416f5a..f74a97c536d 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowValidationNegativeSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowValidationNegativeSpec.groovy @@ -154,9 +154,7 @@ class FlowValidationNegativeSpec extends HealthCheckSpecification { def "Able to detect discrepancies for a flow with protected path"() { when: "Create a flow with protected path" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair) flow.allocateProtectedPath = true flowHelperV2.addFlow(flow) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/IntentionalRerouteSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/IntentionalRerouteSpec.groovy index dfb1e58a27f..56dfeabf629 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/IntentionalRerouteSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/IntentionalRerouteSpec.groovy @@ -37,8 +37,7 @@ class IntentionalRerouteSpec extends HealthCheckSpecification { @Tags(ISL_PROPS_DB_RESET) def "Not able to reroute to a path with not enough bandwidth available"() { given: "A flow with alternate paths available" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair) flow.maximumBandwidth = 10000 flowHelperV2.addFlow(flow) @@ -86,8 +85,7 @@ class IntentionalRerouteSpec extends HealthCheckSpecification { @Tags(ISL_PROPS_DB_RESET) def "Able to reroute to a better path if it has enough bandwidth"() { given: "A flow with alternate paths available" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair) flow.maximumBandwidth = 10000 flow.encapsulationType = FlowEncapsulationType.TRANSIT_VLAN @@ -201,8 +199,7 @@ class IntentionalRerouteSpec extends HealthCheckSpecification { @Tags(ISL_PROPS_DB_RESET) def "Able to reroute to a path with not enough bandwidth available in case ignoreBandwidth=true"() { given: "A flow with alternate paths available" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair) flow.maximumBandwidth = 10000 flow.ignoreBandwidth = true @@ -262,16 +259,7 @@ class IntentionalRerouteSpec extends HealthCheckSpecification { @Tags(HARDWARE) def "Intentional flow reroute with VXLAN encapsulation is not causing any packet loss"() { given: "A vxlan flow" - def allTraffgenSwitchIds = topology.activeTraffGens*.switchConnected.findAll { - switchHelper.isVxlanEnabled(it.dpId) - }*.dpId ?: assumeTrue(false, "Should be at least two active traffgens connected to NoviFlow switches") - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { swP -> - allTraffgenSwitchIds.contains(swP.src.dpId) && - allTraffgenSwitchIds.contains(swP.dst.dpId) && - swP.paths.findAll { path -> - pathHelper.getInvolvedSwitches(path).every { switchHelper.isVxlanEnabled(it.dpId) } - }.size() > 1 - } ?: assumeTrue(false, "Unable to find required switches/paths in topology") + def switchPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().withTraffgensOnBothEnds().random() def availablePaths = switchPair.paths.findAll { pathHelper.getInvolvedSwitches(it).find { it.noviflow }} def flow = flowHelperV2.randomFlow(switchPair) @@ -318,8 +306,7 @@ class IntentionalRerouteSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY, ISL_PROPS_DB_RESET]) def "Not able to reroute to a path with not enough bandwidth available [v1 api]"() { given: "A flow with alternate paths available" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelper.randomFlow(switchPair) flow.maximumBandwidth = 10000 flowHelper.addFlow(flow) @@ -365,8 +352,7 @@ class IntentionalRerouteSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY, ISL_PROPS_DB_RESET]) def "Able to reroute to a better path if it has enough bandwidth [v1 api]"() { given: "A flow with alternate paths available" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelper.randomFlow(switchPair) flow.maximumBandwidth = 10000 flowHelper.addFlow(flow) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MaxLatencySpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MaxLatencySpec.groovy index 7e9445f92ff..956c254336c 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MaxLatencySpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MaxLatencySpec.groovy @@ -54,11 +54,8 @@ class MaxLatencySpec extends HealthCheckSpecification { def setupSpec() { //setup: Two active switches with two diverse paths - List> paths - switchPair = topologyHelper.switchPairs.find { - paths = it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 } - paths.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + switchPair = switchPairs.all(false).withAtLeastNNonOverlappingPaths(2).first() + def paths = switchPair.getPaths().unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 } mainPath = paths[0] alternativePath = paths[1] mainIsls = pathHelper.getInvolvedIsls(mainPath) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MirrorEndpointsSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MirrorEndpointsSpec.groovy index 118c01b05a0..7b195769161 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MirrorEndpointsSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MirrorEndpointsSpec.groovy @@ -231,10 +231,11 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Can create mirror point on protected flow and survive path swap, #mirrorDirection"() { given: "A flow with protected path" - SwitchPair swPair = topologyHelper.getSwitchPairs(true).find { SwitchPair pair -> - pair.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 && - [pair.src, pair.dst].every { it.traffGens } && pair.src.traffGens.size() > 1 - } ?: assumeTrue(false, "Unable to find a switch pair with 2+ diverse paths and 3+ traffgens") + SwitchPair swPair = switchPairs.all() + .withTraffgensOnBothEnds() + .withAtLeastNTraffgensOnSource(2) + .withAtLeastNNonOverlappingPaths(2) + .random() def flow = flowHelperV2.randomFlow(swPair).tap { it.allocateProtectedPath = true } flowHelperV2.addFlow(flow) @@ -367,13 +368,11 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { where: [data, mirrorDirection] << [ [[ - swPair: topologyHelper.getSwitchPairs(true).find { swP -> - swP.paths.find { pathHelper.getInvolvedSwitches(it).every { switchHelper.isVxlanEnabled(it.dpId) } } - }, + swPair: switchPairs.all().withBothSwitchesVxLanEnabled().random(), encap : FlowEncapsulationType.VXLAN ], [ - swPair: topologyHelper.switchPairs[0], + swPair: switchPairs.all().random(), encap : FlowEncapsulationType.TRANSIT_VLAN ]], //^run all direction combinations for above data @@ -385,7 +384,7 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Can create mirror point on unmetered pinned flow, #mirrorDirection"() { given: "An unmetered pinned flow" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair).tap { pinned = true maximumBandwidth = 0 @@ -474,7 +473,7 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Can create multiple mirror points for the same flow and switch"() { given: "A flow" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) @@ -544,7 +543,7 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { def "System also updates mirror rule after flow partial update"() { given: "A flow with mirror point" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) def freePort = (topology.getAllowedPortsForSwitch(swPair.dst) - flow.destination.portNumber)[0] @@ -591,10 +590,9 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Mirror point can be created for a default flow (0 vlan), #mirrorDirection"() { given: "A default flow" - def swPair = topologyHelper.getSwitchPairs(true).find { SwitchPair pair -> - [pair.src, pair.dst].every { it.traffGens } && pair.src.traffGens.size() > 1 - } - assumeTrue(swPair as boolean, "Unable to find a switch pair: both with traffgens, src with at least 2") + def swPair = switchPairs.all() + .withTraffgensOnBothEnds() + .withAtLeastNTraffgensOnSource(2).random() def flow = flowHelperV2.randomFlow(swPair).tap { source.vlanId = 0 } flowHelperV2.addFlow(flow) @@ -635,9 +633,10 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Flow mirror point works properly with a qinq flow, #mirrorDirection"() { given: "A qinq flow" - def swPair = topologyHelper.getSwitchPairs(true).find { - [it.src, it.dst].every { it.traffGens} && it.src.traffGens.size() > 1 - } ?: assumeTrue(false, "Not able to find enough switches with traffgens and in multi-table mode") + def swPair = switchPairs.all() + .withTraffgensOnBothEnds() + .withAtLeastNTraffgensOnSource(2) + .random() def flow = flowHelperV2.randomFlow(swPair).tap { source.innerVlanId = 100 destination.innerVlanId = 200 @@ -680,15 +679,12 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Unable to create a mirror endpoint with #data.testDescr on the transit switch"() { given: "A flow with transit switch" - List path + def swPair = switchPairs.all().nonNeighbouring().random() List involvedSwitches - def swPair = topologyHelper.switchPairs.find { - path = it.paths.find { - involvedSwitches = pathHelper.getInvolvedSwitches(it) - involvedSwitches.size() > 2 - } + List path = swPair.getPaths().find { + involvedSwitches = pathHelper.getInvolvedSwitches(it) + involvedSwitches.size() == 3 } - assumeTrue(swPair as boolean, "Unable to find a switch pair with path of 2+ switches") def flow = flowHelperV2.randomFlow(swPair) swPair.paths.findAll { it != path }.each { pathHelper.makePathMorePreferable(path, it) } flowHelperV2.addFlow(flow) @@ -761,7 +757,7 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { where: testData << [ new MirrorErrorTestData("Unable to create a mirror endpoint on the src sw and sink back to dst sw", { - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() it.flow = flowHelperV2.randomFlow(swPair) def freePort = (topology.getAllowedPortsForSwitch(swPair.src) - flow.source.portNumber)[0] it.mirrorPoint = FlowMirrorPointPayload.builder() @@ -777,7 +773,7 @@ class MirrorEndpointsSpec extends HealthCheckSpecification { the sink switch id cannot differ from the mirror point switch id./) }), new MirrorErrorTestData("Unable to create a mirror point with isl conflict", { - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() it.flow = flowHelperV2.randomFlow(swPair) def islPort = topology.getBusyPortsForSwitch(swPair.dst)[0] it.mirrorPoint = FlowMirrorPointPayload.builder() @@ -793,10 +789,7 @@ the sink switch id cannot differ from the mirror point switch id./) \'$swPair.dst.dpId\' is occupied by an ISL \(destination endpoint collision\)./) }), new MirrorErrorTestData("Unable to create a mirror point with s42Port conflict", { - def s42Switches = topology.getActiveServer42Switches()*.dpId - def swPair = topologyHelper.getSwitchPairs(true).find { - it.dst.dpId in s42Switches - } + def swPair = switchPairs.all().withDestinationSwitchConnectedToServer42().random() it.flow = flowHelperV2.randomFlow(swPair) def s42Port = swPair.dst.prop.server42Port it.mirrorPoint = FlowMirrorPointPayload.builder() @@ -821,7 +814,7 @@ with these parameters./) @Tags([LOW_PRIORITY]) def "Unable to create a mirror point with existing flow conflict, #mirrorDirection"() { given: "A flow" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) def otherFlow = flowHelperV2.randomFlow(swPair, false, [flow]) flowHelperV2.addFlow(flow) @@ -854,7 +847,7 @@ with these parameters./) @Tags([LOW_PRIORITY]) def "Unable to create a flow that conflicts with mirror point, #mirrorDirection"() { given: "A flow with mirror point" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) def freePort = (topology.getAllowedPortsForSwitch(swPair.dst) - flow.destination.portNumber)[0] @@ -896,7 +889,7 @@ with these parameters./) @Tags([LOW_PRIORITY]) def "Unable to create mirror point with connected devices enabled, #mirrorDirection"() { given: "A flow with connected devices enabled" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair).tap { source.detectConnectedDevices = new DetectConnectedDevicesV2(true, true) } @@ -931,7 +924,7 @@ flow mirror point cannot be created this flow/).matches(error) @Tags([LOW_PRIORITY]) def "Unable to update flow and enable connected devices if mirror is present, #mirrorDirection"() { given: "A flow with a mirror point" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) def freePort = (topology.getAllowedPortsForSwitch(swPair.src) - flow.source.portNumber)[0] @@ -968,7 +961,7 @@ flow mirror point cannot be created this flow/).matches(error) @Tags([LOW_PRIORITY]) def "Cannot enable connected devices on switch if mirror is present"() { given: "A flow with a mirror endpoint" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) def freePort = (topology.getAllowedPortsForSwitch(swPair.src) - flow.source.portNumber)[0] @@ -1003,7 +996,7 @@ flow mirror point cannot be created this flow/).matches(error) @Tags([LOW_PRIORITY]) def "Cannot create mirror on a switch with enabled connected devices"() { given: "A switch with enabled connected devices" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def originalProps = switchHelper.getCachedSwProps(swPair.src.dpId) northbound.updateSwitchProperties(swPair.src.dpId, originalProps.jacksonCopy().tap { it.switchArp = true @@ -1038,7 +1031,7 @@ flow mirror point cannot be created this flow/).matches(error) @Tags([LOW_PRIORITY]) def "Unable to create a mirror point with existing mirror point conflict, #mirrorDirection"() { given: "A flow with mirror point" - def swPair = topologyHelper.switchPairs[0] + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) def freePort = (topology.getAllowedPortsForSwitch(swPair.dst) - flow.destination.portNumber)[0] @@ -1158,7 +1151,7 @@ with existing flow mirror point \'$existingMirror.mirrorPointId\'./ List getUniqueSwitchPairs(Closure additionalConditions = { true }) { def unpickedUniqueTgSwitches = topology.activeSwitches.findAll { it.traffGens } .unique(false) { it.hwSwString } - def tgPairs = topologyHelper.getSwitchPairs(true).findAll { + def tgPairs = switchPairs.all().getSwitchPairs().findAll { additionalConditions(it) } assumeTrue(tgPairs.size() > 0, "Unable to find any switchPairs with requested conditions") diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MultiRerouteSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MultiRerouteSpec.groovy index 34195254e82..e130def8bef 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MultiRerouteSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/MultiRerouteSpec.groovy @@ -19,8 +19,7 @@ class MultiRerouteSpec extends HealthCheckSpecification { @Tags([ISL_RECOVER_ON_FAIL, ISL_PROPS_DB_RESET]) def "Simultaneous reroute of multiple flows should not oversubscribe any ISLs"() { given: "Many flows on the same path, with alt paths available" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 2 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(3).first() List flows = [] def currentPath = switchPair.paths.first() switchPair.paths.findAll { it != currentPath }.each { pathHelper.makePathMorePreferable(currentPath, it) } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PartialUpdateSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PartialUpdateSpec.groovy index db72b450a62..f808edb20e9 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PartialUpdateSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PartialUpdateSpec.groovy @@ -49,7 +49,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { def "Able to partially update flow '#data.field' without reinstalling its rules"() { given: "A flow" - def swPair = topologyHelper.switchPairs.first() + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flow.tap{ pathComputationStrategy = "cost" @@ -121,7 +121,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Able to partially update flow #data.field without reinstalling its rules(v1)"() { given: "A flow" - def swPair = topologyHelper.switchPairs.first() + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) def originalCookies = northbound.getSwitchRules(swPair.src.dpId).flowEntries.findAll { @@ -168,7 +168,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { def "Able to partially update flow #data.field which causes a reroute"() { given: "A flow" - def swPair = topologyHelper.switchPairs.first() + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) def originalCookies = northbound.getSwitchRules(swPair.src.dpId).flowEntries.findAll { !new Cookie(it.cookie).serviceFlag } @@ -210,9 +210,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { def "Able to turn on diversity feature using partial update"() { given: "Two active neighboring switches with two not overlapping paths at least" - def switchPair = topologyHelper.switchPairs.find { - it.paths.collect { pathHelper.getInvolvedIsls(it) }.unique { a, b -> a.intersect(b) ? 0 : 1 }.size() >= 2 - } ?: assumeTrue(false, "Can't find a switch pair with 2 not overlapping paths") + def switchPair = switchPairs.all().withAtLeastNNonOverlappingPaths(2).random() when: "Create 2 not diverse flows going through these switches" def flow1 = flowHelperV2.randomFlow(switchPair) @@ -370,11 +368,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { def "Able to update flow encapsulationType using partial update"() { given: "A flow with a 'transit_vlan' encapsulation" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { swP -> - [swP.src, swP.dst].every { switchHelper.isVxlanEnabled(it.dpId) } - } - assumeTrue(switchPair as boolean, "Unable to find required switches in topology") - + def switchPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().random() def flow = flowHelperV2.randomFlow(switchPair) flow.encapsulationType = FlowEncapsulationType.TRANSIT_VLAN flowHelperV2.addFlow(flow) @@ -523,7 +517,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "Partial update with empty body does not actually update flow in any way(v1)"() { given: "A flow" - def swPair = topologyHelper.switchPairs.first() + def swPair = switchPairs.all().random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) def originalCookies = northbound.getSwitchRules(swPair.src.dpId).flowEntries.findAll { @@ -552,9 +546,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { def "Partial update with empty body does not actually update flow in any way"() { given: "A flow" - def swPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.collect { pathHelper.getInvolvedIsls(it) }.unique { a, b -> a.intersect(b) ? 0 : 1 }.size() > 1 - } ?: assumeTrue(false, "Need at least 2 non-overlapping paths for diverse flow") + def swPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() def helperFlow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(helperFlow) def flow = flowHelperV2.randomFlow(swPair).tap { @@ -628,7 +620,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { @Unroll("Unable to partial update flow (#data.conflict)") def "Unable to partial update flow when there are conflicting vlans"() { given: "Two potential flows" - def swPair = topologyHelper.switchPairs.first() + def swPair = switchPairs.all().random() def flow1 = flowHelperV2.randomFlow(swPair, false) def flow2 = flowHelperV2.randomFlow(swPair, false, [flow1]) FlowPatchV2 patch = data.getPatch(flow1) @@ -708,7 +700,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { def "Unable to update a flow to have both strict_bandwidth and ignore_bandwidth flags at the same time"() { given: "An existing flow without flag conflicts" - def flow = flowHelperV2.randomFlow(topologyHelper.switchPairs[0]).tap { + def flow = flowHelperV2.randomFlow(switchPairs.all().random()).tap { ignoreBandwidth = initialIgnore strictBandwidth = initialStrict } @@ -738,7 +730,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { @Tags(LOW_PRIORITY) def "Able to update vlanId via partialUpdate in case vlanId==0 and innerVlanId!=0"() { given: "A default flow" - def swPair = topologyHelper.switchPairs.first() + def swPair = switchPairs.all().random() def defaultFlow = flowHelperV2.randomFlow(swPair).tap { source.vlanId = 0 source.innerVlanId = 0 @@ -771,7 +763,7 @@ class PartialUpdateSpec extends HealthCheckSpecification { @Unroll("Unable to partial update flow (maxLatency #maxLatencyAfter and maxLatencyTier2 #maxLatencyT2After)") def "Unable to partial update flow with maxLatency incorrect value"() { given: "Two potential flows" - def flow = flowHelperV2.randomFlow(topologyHelper.switchPairs[0]).tap { + def flow = flowHelperV2.randomFlow(switchPairs.all().random()).tap { maxLatency = maxLatencyBefore maxLatencyTier2 = maxLatencyT2Before diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PinnedFlowSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PinnedFlowSpec.groovy index 7ab83765b05..02057150093 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PinnedFlowSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PinnedFlowSpec.groovy @@ -81,8 +81,7 @@ class PinnedFlowSpec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "System doesn't reroute(automatically) pinned flow when flow path is partially broken"() { given: "A pinned flow going through a long not preferable path" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNPaths(2).random() List> allPaths = database.getPaths(switchPair.src.dpId, switchPair.dst.dpId)*.path def longestPath = allPaths.max { it.size() } allPaths.findAll { it != longestPath }.collect { pathHelper.makePathMorePreferable(longestPath, it) } @@ -177,8 +176,7 @@ class PinnedFlowSpec extends HealthCheckSpecification { def "System is not rerouting pinned flow when 'reroute link flows' is called"() { given: "A pinned flow with alt path available" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair).tap { it.pinned = true } flowHelperV2.addFlow(flow) def currentPath = pathHelper.convert(northbound.getFlowPath(flow.flowId)) @@ -206,8 +204,7 @@ class PinnedFlowSpec extends HealthCheckSpecification { def "System returns error if trying to intentionally reroute a pinned flow"() { given: "A pinned flow with alt path available" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair).tap { it.pinned = true } flowHelperV2.addFlow(flow) def currentPath = pathHelper.convert(northbound.getFlowPath(flow.flowId)) @@ -229,8 +226,7 @@ class PinnedFlowSpec extends HealthCheckSpecification { def "System doesn't allow to create pinned and protected flow at the same time"() { when: "Try to create pinned and protected flow" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair) flow.pinned = true flow.allocateProtectedPath = true @@ -249,8 +245,7 @@ class PinnedFlowSpec extends HealthCheckSpecification { def "System doesn't allow to enable the protected path flag on a pinned flow"() { given: "A pinned flow" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair) flow.pinned = true flowHelperV2.addFlow(flow) @@ -272,8 +267,7 @@ class PinnedFlowSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "System doesn't allow to create pinned and protected flow at the same time [v1 api]"() { when: "Try to create pinned and protected flow" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelper.randomFlow(switchPair) flow.pinned = true flow.allocateProtectedPath = true @@ -293,8 +287,7 @@ class PinnedFlowSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY]) def "System doesn't allow to enable the protected path flag on a pinned flow [v1 api]"() { given: "A pinned flow" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelper.randomFlow(switchPair) flow.pinned = true flowHelper.addFlow(flow) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ProtectedPathSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ProtectedPathSpec.groovy index 74f813e6ee7..0a859c85087 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ProtectedPathSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ProtectedPathSpec.groovy @@ -15,6 +15,7 @@ import static org.openkilda.model.cookie.CookieBase.CookieType.SERVICE_OR_FLOW_S import static org.openkilda.testing.Constants.NON_EXISTENT_FLOW_ID import static org.openkilda.testing.Constants.PATH_INSTALLATION_TIME import static org.openkilda.testing.Constants.PROTECTED_PATH_INSTALLATION_TIME +import static org.openkilda.testing.Constants.RULES_INSTALLATION_TIME import static org.openkilda.testing.Constants.WAIT_OFFSET import org.openkilda.functionaltests.HealthCheckSpecification @@ -67,9 +68,7 @@ class ProtectedPathSpec extends HealthCheckSpecification { @Tags(LOW_PRIORITY) def "Able to create a flow with protected path when maximumBandwidth=#bandwidth, vlan=#vlanId"() { given: "Two active not neighboring switches with two diverse paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() when: "Create flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -105,9 +104,7 @@ class ProtectedPathSpec extends HealthCheckSpecification { @Tags([SMOKE_SWITCHES, SMOKE]) def "Able to enable/disable protected path on a flow"() { given: "Two active not neighboring switches with two diverse paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() when: "Create flow without protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -163,13 +160,11 @@ class ProtectedPathSpec extends HealthCheckSpecification { def "Able to swap main and protected paths manually"() { given: "A simple flow" def tgSwitches = topology.getActiveTraffGens()*.getSwitchConnected() - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - def allowsTraffexam = it.src in tgSwitches && it.dst in tgSwitches - def usesUniqueSwitches = it.paths.collectMany { pathHelper.getInvolvedSwitches(it) } - .unique { it.dpId }.size() > 3 - return allowsTraffexam && usesUniqueSwitches - } ?: assumeTrue(false, "No suiting switches found") - + def switchPair = switchPairs.all() + .nonNeighbouring() + .withTraffgensOnBothEnds() + .withPathHavingAtLeastNSwitches(4) + .random() def flow = flowHelperV2.randomFlow(switchPair, true) flow.allocateProtectedPath = false flowHelperV2.addFlow(flow) @@ -330,9 +325,7 @@ class ProtectedPathSpec extends HealthCheckSpecification { def "System is able to switch #flowDescription flows to protected paths"() { given: "Two active not neighboring switches with three diverse paths at least" def initialIsls = northbound.getAllLinks() - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 3 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(3).random() def uniquePathCount = switchPair.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() when: "Create 5 flows with protected paths" @@ -419,9 +412,7 @@ class ProtectedPathSpec extends HealthCheckSpecification { @Tags([ISL_RECOVER_ON_FAIL, ISL_PROPS_DB_RESET]) def "Flow swaps to protected path when main path gets broken, becomes DEGRADED if protected path is unable to reroute(no bw)"() { given: "Two switches with 2 diverse paths at least" - def switchPair = topologyHelper.switchPairs.find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() > 1 - } ?: assumeTrue(false, "No switches with at least 2 diverse paths") + def switchPair = switchPairs.all().withAtLeastNNonOverlappingPaths(2).random() when: "Create flow with protected path" def flow = flowHelperV2.randomFlow(switchPair).tap { allocateProtectedPath = true } @@ -480,9 +471,7 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban @Tags(ISL_RECOVER_ON_FAIL) def "Flow swaps to protected path when main path gets broken, becomes DEGRADED if protected path is unable to reroute(no path)"() { given: "Two switches with 2 diverse paths at least" - def switchPair = topologyHelper.switchPairs.find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() > 1 - } ?: assumeTrue(false, "No switches with at least 2 diverse paths") + def switchPair = switchPairs.all().withAtLeastNNonOverlappingPaths(2).random() when: "Create flow with protected path" def flow = flowHelperV2.randomFlow(switchPair).tap { allocateProtectedPath = true } @@ -546,9 +535,7 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban is intentional"() { // 'and ignores protected path' means that the main path won't changed to protected given: "Two active neighboring switches with four diverse paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 4 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(4).random() and: "A flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -603,9 +590,7 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban def "System is able to switch #flowDescription flow to protected path and ignores more preferable path when reroute\ is automatical"() { given: "Two active not neighboring switches with three diverse paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 3 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(3).random() def uniquePathCount = switchPair.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() @@ -767,11 +752,9 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban @Tags(ISL_RECOVER_ON_FAIL) def "System is able to recalculate protected path when protected path is broken"() { - given: "Two active not neighboring switches with two diverse paths at least" + given: "Two active not neighboring switches with three diverse paths at least" def allIsls = northbound.getAllLinks() - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 3 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(3).random() when: "Create a flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -865,9 +848,8 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban @IterationTag(tags = [LOW_PRIORITY], iterationNameRegex = /unmetered/) def "Able to update #flowDescription flow to enable protected path if all alternative paths are unavailable"() { given: "Two active neighboring switches with two not overlapping paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() + and: "A flow without protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -917,9 +899,8 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban @IterationTag(tags = [LOW_PRIORITY], iterationNameRegex = /unmetered/) def "#flowDescription flow is DEGRADED when protected and alternative paths are not available"() { given: "Two active neighboring switches with two not overlapping paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() + and: "A flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -982,9 +963,7 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban @Tags(ISL_RECOVER_ON_FAIL) def "System properly reroutes both paths if protected path breaks during auto-swap"() { given: "Switch pair with at least 4 diverse paths" - def switchPair = topologyHelper.switchPairs.find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 4 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all(false).withAtLeastNNonOverlappingPaths(4).random() and: "A protected flow" def flow = flowHelperV2.randomFlow(switchPair).tap { it.allocateProtectedPath = true } @@ -1034,13 +1013,10 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban } } - @Ignore("https://github.com/telstra/open-kilda/issues/2762") def "System reuses current protected path when can't find new non overlapping protected path while intentional\ rerouting"() { given: "Two active neighboring switches with three diverse paths" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() == 3 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withExactlyNNonOverlappingPaths(3).random() and: "A flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -1056,8 +1032,10 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban when: "Make the current and protected path less preferable than alternatives" def alternativePaths = switchPair.paths.findAll { it != currentPath && it != currentProtectedPath } - alternativePaths.each { pathHelper.makePathMorePreferable(it, currentPath) } - alternativePaths.each { pathHelper.makePathMorePreferable(it, currentProtectedPath) } + withPool { + alternativePaths.eachParallel { pathHelper.makePathMorePreferable(it, currentPath) } + alternativePaths.eachParallel { pathHelper.makePathMorePreferable(it, currentProtectedPath) } + } and: "Init intentional reroute" def rerouteResponse = northboundV2.rerouteFlow(flow.flowId) @@ -1066,10 +1044,14 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban rerouteResponse.rerouted and: "Flow main path should be rerouted to a new path and ignore protected path" - def flowPathInfoAfterRerouting = northbound.getFlowPath(flow.flowId) - def newCurrentPath = pathHelper.convert(flowPathInfoAfterRerouting) - newCurrentPath != currentPath - newCurrentPath != currentProtectedPath + def flowPathInfoAfterRerouting + def newCurrentPath + Wrappers.wait(RULES_INSTALLATION_TIME) { + flowPathInfoAfterRerouting = northbound.getFlowPath(flow.flowId) + newCurrentPath = pathHelper.convert(flowPathInfoAfterRerouting) + newCurrentPath != currentPath + newCurrentPath != currentProtectedPath + } and: "Flow protected path shouldn't be rerouted due to lack of non overlapping path" pathHelper.convert(flowPathInfoAfterRerouting.protectedPath) == currentProtectedPath @@ -1094,7 +1076,7 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban def allPaths // all possible paths def allPathsWithThreeSwitches //all possible paths with 3 involved switches def allPathCandidates // 3 diverse paths at least - def swPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { swP -> + def swPair = switchPairs.all().nonNeighbouring().getSwitchPairs().find { swP -> allPaths = swP.paths allPathsWithThreeSwitches = allPaths.findAll { pathHelper.getInvolvedSwitches(it).size() == 3 } allPathCandidates = allPathsWithThreeSwitches.unique(false) { a, b -> @@ -1288,9 +1270,7 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban @Tags([LOW_PRIORITY, ISL_RECOVER_ON_FAIL]) def "Unable to swap paths for an inactive flow"() { given: "Two active neighboring switches with two not overlapping paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() and: "A flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -1495,9 +1475,8 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban def "System doesn't reroute main flow path when protected path is broken and new alt path is available\ (altPath is more preferable than mainPath)"() { given: "Two active neighboring switches with three diverse paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 3 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withExactlyNNonOverlappingPaths(3).random() + and: "A flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -1566,8 +1545,7 @@ doesn't have links with enough bandwidth, Failed to find path with requested ban @Tags(LOW_PRIORITY) def "System doesn't allow to enable the pinned flag on a protected flow"() { given: "A protected flow" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(switchPair) flow.allocateProtectedPath = true flowHelperV2.addFlow(flow) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ProtectedPathV1Spec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ProtectedPathV1Spec.groovy index e218e244612..5c6b0a5a6e6 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ProtectedPathV1Spec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ProtectedPathV1Spec.groovy @@ -57,9 +57,7 @@ class ProtectedPathV1Spec extends HealthCheckSpecification { def "Able to create a flow with protected path when maximumBandwidth=#bandwidth, vlan=#vlanId"() { given: "Two active not neighboring switches with two diverse paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() when: "Create flow with protected path" def flow = flowHelper.randomFlow(switchPair) @@ -92,9 +90,7 @@ class ProtectedPathV1Spec extends HealthCheckSpecification { def "Able to enable/disable protected path on a flow"() { given: "Two active not neighboring switches with two diverse paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() when: "Create flow without protected path" def flow = flowHelper.randomFlow(switchPair) @@ -269,13 +265,11 @@ class ProtectedPathV1Spec extends HealthCheckSpecification { def "Able to swap main and protected paths manually"() { given: "A simple flow" def tgSwitches = topology.getActiveTraffGens()*.getSwitchConnected() - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - def allowsTraffexam = it.src in tgSwitches && it.dst in tgSwitches - def usesUniqueSwitches = it.paths.collectMany { pathHelper.getInvolvedSwitches(it) } - .unique { it.dpId }.size() > 3 - return allowsTraffexam && usesUniqueSwitches - } ?: assumeTrue(false, "No suiting switches found") - + def switchPair = switchPairs.all() + .nonNeighbouring() + .withTraffgensOnBothEnds() + .withPathHavingAtLeastNSwitches(4) + .random() def flow = flowHelper.randomFlow(switchPair, true) flow.allocateProtectedPath = false flowHelper.addFlow(flow) @@ -470,9 +464,7 @@ class ProtectedPathV1Spec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "Unable to swap paths for an inactive flow"() { given: "Two active neighboring switches with two not overlapping paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() and: "A flow with protected path" def flow = flowHelper.randomFlow(switchPair) @@ -582,8 +574,7 @@ class ProtectedPathV1Spec extends HealthCheckSpecification { def "System doesn't allow to enable the pinned flag on a protected flow"() { given: "A protected flow" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def flow = flowHelper.randomFlow(switchPair) flow.allocateProtectedPath = true flowHelper.addFlow(flow) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/QinQFlowSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/QinQFlowSpec.groovy index d2e5c2d051a..825d15034d5 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/QinQFlowSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/QinQFlowSpec.groovy @@ -318,7 +318,7 @@ class QinQFlowSpec extends HealthCheckSpecification { def "System doesn't allow to create a QinQ flow with incorrect innerVlanIds\ (src:#srcInnerVlanId, dst:#dstInnerVlanId)"() { given: "A switch pair with enabled multi table mode" - def swP = topologyHelper.getAllNeighboringSwitchPairs()[0] + def swP = switchPairs.all().neighbouring().random() when: "Try to create a QinQ flow with incorrect innerVlanId" def flow = flowHelperV2.randomFlow(swP) @@ -345,7 +345,7 @@ class QinQFlowSpec extends HealthCheckSpecification { */ def "Flow with innerVlan and vlanId=0 is transformed into a regular vlan flow without innerVlan"() { when: "Create a flow with vlanId=0 and innerVlanId!=0" - def swP = topologyHelper.switchPairs[0] + def swP = switchPairs.all().random() def flow = flowHelper.randomFlow(swP) flow.source.vlanId = 0 flow.source.innerVlanId = 123 @@ -366,9 +366,7 @@ class QinQFlowSpec extends HealthCheckSpecification { def "System allow to create/update/delete a protected QinQ flow via APIv1"() { given: "Two switches with enabled multi table mode" - def swP = topologyHelper.getSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "Not able to find enough switches with 2 diverse paths") + def swP = switchPairs.all().withAtLeastNNonOverlappingPaths(2).random() when: "Create a QinQ flow" def flow = flowHelper.randomFlow(swP) @@ -436,9 +434,7 @@ class QinQFlowSpec extends HealthCheckSpecification { def "System allows to create QinQ flow and vlan flow with the same vlan on the same port"() { given: "Two switches with enabled multi table mode" - def swP = topologyHelper.getAllNeighboringSwitchPairs().find { - it.src.traffGens && it.dst.traffGens - } ?: assumeTrue(false, "Not able to find enough switches with traffgens") + def swP = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random() when: "Create a QinQ flow" def flowWithQinQ = flowHelperV2.randomFlow(swP) @@ -474,7 +470,7 @@ class QinQFlowSpec extends HealthCheckSpecification { def "System detects conflict QinQ flows(oVlan: #conflictVlan, iVlan: #conflictInnerVlanId)"() { given: "Two switches with enabled multi table mode" - def swP = topologyHelper.getAllNeighboringSwitchPairs()[0] + def swP = switchPairs.all().neighbouring().random() when: "Create a first flow" def flow = flowHelperV2.randomFlow(swP) @@ -505,9 +501,7 @@ class QinQFlowSpec extends HealthCheckSpecification { def "System allows to create more than one QinQ flow on the same port and with the same vlan"() { given: "Two switches connected to traffgen and enabled multiTable mode" - def swP = topologyHelper.getAllNeighboringSwitchPairs().find { - it.src.traffGens && it.dst.traffGens - } ?: assumeTrue(false, "Not able to find enough switches with traffgens") + def swP = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random() when: "Create a first QinQ flow" def flow1 = flowHelperV2.randomFlow(swP) @@ -809,21 +803,17 @@ class QinQFlowSpec extends HealthCheckSpecification { [srcVlanId, srcInnerVlanId, dstVlanId, dstInnerVlanId, swPair] << [ [[10, 20, 30, 40], [10, 20, 0, 0]], - getUniqueSwitchPairs(topologyHelper.getSwitchPairs().findAll { SwitchPair swP -> - def allTraffGenSwitchIds = getTopology().getActiveTraffGens()*.switchConnected*.dpId - [swP.src, swP.dst].every { it.dpId in allTraffGenSwitchIds } && - swP.paths.find { - pathHelper.getInvolvedSwitches(it).every { switchHelper.isVxlanEnabled(it.dpId) } - }}) + getUniqueSwitchPairs(switchPairs.all() + .withTraffgensOnBothEnds() + .withBothSwitchesVxLanEnabled() + .getSwitchPairs()) ].combinations().collect { it.flatten() } trafficDisclaimer = swPair.src.traffGens && swPair.dst.traffGens ? "" : " !WARN: No traffic check!" } def "System is able to synchronize switch(flow rules)"() { given: "Two switches connected to traffgen and enabled multiTable mode" - def swP = topologyHelper.getAllNeighboringSwitchPairs().find { - it.src.traffGens && it.dst.traffGens - } ?: assumeTrue(false, "Not able to find enough switches with traffgens") + def swP = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random() and: "A QinQ flow on the given switches" def flow = flowHelperV2.randomFlow(swP) @@ -873,9 +863,10 @@ class QinQFlowSpec extends HealthCheckSpecification { def "System doesn't rebuild flow path to more preferable path while updating innerVlanId"() { given: "Two active switches connected to traffgens with two possible paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.src.traffGens && it.dst.traffGens && it.paths.size() > 2 - } ?: assumeTrue(false, "Not able to find enough switches with traffgens and diverse paths") + def switchPair = switchPairs.all().neighbouring() + .withTraffgensOnBothEnds() + .withAtLeastNPaths(2) + .random() and: "A flow" def flow = flowHelperV2.randomFlow(switchPair) @@ -938,7 +929,7 @@ class QinQFlowSpec extends HealthCheckSpecification { } @Memoized - List getUniqueSwitchPairs(List suitablePairs = topologyHelper.getSwitchPairs(true)) { + List getUniqueSwitchPairs(List suitablePairs = switchPairs.all().getSwitchPairs()) { def unpickedUniqueSwitches = topology.activeSwitches.collect { it.hwSwString }.unique(false) def unpickedSuitableSwitches = unpickedUniqueSwitches.intersect( suitablePairs.collectMany { [it.src.hwSwString, it.dst.hwSwString] }.unique(false)) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/SwapEndpointSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/SwapEndpointSpec.groovy index 13fdbd88085..3a0db3d5cfe 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/SwapEndpointSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/SwapEndpointSpec.groovy @@ -1,5 +1,7 @@ package org.openkilda.functionaltests.spec.flows +import org.openkilda.functionaltests.error.flow.FlowEndpointsNotSwappedExpectedError + import static groovyx.gpars.GParsPool.withPool import static org.junit.jupiter.api.Assumptions.assumeTrue import static org.openkilda.functionaltests.extension.tags.Tag.ISL_RECOVER_ON_FAIL @@ -80,7 +82,7 @@ class SwapEndpointSpec extends HealthCheckSpecification { where: data << [ [description: "no vlan vs vlan on the same port on src switch"].tap { - def switchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def switchPair = getSwitchPairs().all().nonNeighbouring().random() def flow1 = getFlowHelper().randomFlow(switchPair) flow1.source.portNumber = getFreePort(switchPair.src, [switchPair.dst]) flow1.source.vlanId = 0 @@ -93,7 +95,7 @@ class SwapEndpointSpec extends HealthCheckSpecification { getFlowHelper().toFlowEndpointV2(flow2.destination)) }, [description: "same port, swap vlans on dst switch + third idle novlan flow on that port"].tap { - def switchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def switchPair = getSwitchPairs().all().nonNeighbouring().random() def flow1 = getFlowHelper().randomFlow(switchPair) def flow2 = getFlowHelper().randomFlow(switchPair, false, [flow1]) flow1.destination.portNumber = getFreePort(switchPair.dst, [switchPair.src]) @@ -109,7 +111,7 @@ class SwapEndpointSpec extends HealthCheckSpecification { getFlowHelper().toFlowEndpointV2(flow1.destination)) }, [description: "vlan on src1 <-> vlan on dst2, same port numbers"].tap { - def switchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def switchPair = getSwitchPairs().all().nonNeighbouring().random() def flow1 = getFlowHelper().randomFlow(switchPair) def flow2 = getFlowHelper().randomFlow(switchPair, false, [flow1]) flow1.source.portNumber = getFreePort(switchPair.src, [switchPair.dst]) @@ -123,7 +125,7 @@ class SwapEndpointSpec extends HealthCheckSpecification { getFlowHelper().toFlowEndpointV2(flow2.destination).tap { it.vlanId = flow1.source.vlanId }) }, [description: "port on dst1 <-> port on src2, vlans are equal"].tap { - def switchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def switchPair = getSwitchPairs().all().nonNeighbouring().random() def flow1 = getFlowHelper().randomFlow(switchPair, false) def flow2 = getFlowHelper().randomFlow(switchPair, false, [flow1]) flow1.destination.portNumber = getFreePort(switchPair.dst, [switchPair.src], @@ -142,7 +144,7 @@ class SwapEndpointSpec extends HealthCheckSpecification { getFlowHelper().toFlowEndpointV2(flow2.destination)) }, [description: "switch on src1 <-> switch on dst2, other params random"].tap { - def switchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def switchPair = getSwitchPairs().all().nonNeighbouring().random() def flow1 = getFlowHelper().randomFlow(switchPair) def flow2 = getFlowHelper().randomFlow(switchPair, false, [flow1]) flow1.source.portNumber = getFreePort(switchPair.src, [switchPair.dst]) @@ -158,7 +160,7 @@ class SwapEndpointSpec extends HealthCheckSpecification { .tap { it.switchId = flow1.source.datapath }) }, [description: "both endpoints swap, same switches"].tap { - def switchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def switchPair = getSwitchPairs().all().nonNeighbouring().random() def flow1 = getFlowHelper().randomFlow(switchPair) def flow2 = getFlowHelper().randomFlow(switchPair, false, [flow1]) flow1.source.portNumber = getFreePort(switchPair.src, [switchPair.dst]) @@ -174,7 +176,7 @@ class SwapEndpointSpec extends HealthCheckSpecification { getFlowHelper().toFlowEndpointV2(flow1.destination)) }, [description: "endpoints src1 <-> dst2, same switches"].tap { - def switchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def switchPair = getSwitchPairs().all().nonNeighbouring().random() def flow1 = getFlowHelper().randomFlow(switchPair) def flow2 = getFlowHelper().randomFlow(switchPair, false, [flow1]) flow1.source.portNumber = getFreePort(switchPair.src, [switchPair.dst]) @@ -192,17 +194,17 @@ class SwapEndpointSpec extends HealthCheckSpecification { getFlowHelper().toFlowEndpointV2(flow1.source)) }, [description: "endpoints src1 <-> src2, different src switches, same dst"].tap { - List switchPairs = getTopologyHelper().getAllNotNeighboringSwitchPairs() + List swPairs = getSwitchPairs().all().nonNeighbouring().getSwitchPairs() .inject(null) { result, switchPair -> if (result) return result def halfDifferent = getHalfDifferentNotNeighboringSwitchPair(switchPair, "dst") if (halfDifferent) result = [switchPair, halfDifferent] return result } - def flow1 = getFlowHelper().randomFlow(switchPairs[0]) - def flow2 = getFlowHelper().randomFlow(switchPairs[1], false, [flow1]) - flow1.source.portNumber = getFreePort(switchPairs[0].src, [switchPairs[1].src]) - flow2.source.portNumber = getFreePort(switchPairs[1].src, [switchPairs[0].src]) + def flow1 = getFlowHelper().randomFlow(swPairs[0]) + def flow2 = getFlowHelper().randomFlow(swPairs[1], false, [flow1]) + flow1.source.portNumber = getFreePort(swPairs[0].src, [swPairs[1].src]) + flow2.source.portNumber = getFreePort(swPairs[1].src, [swPairs[0].src]) it.flows = [flow1, flow2] it.firstSwap = new SwapFlowPayload(flow1.id, getFlowHelper().toFlowEndpointV2(flow2.source), @@ -267,15 +269,15 @@ switches"() { it.flow2Src = changePropertyValue(it.flow2.source, "datapath", it.flow1.destination.datapath) it.flow2Dst = changePropertyValue(it.flow2.destination, "datapath", it.flow1.source.datapath) }].collect { iterationData -> - def swPairs = switchPairs.all().nonNeighbouring().getSwitchPairs().inject(null) { result, switchPair -> + def switchPairs = getSwitchPairs().all().nonNeighbouring().getSwitchPairs().inject(null) { result, switchPair -> if (result) return result def halfDifferent = getHalfDifferentNotNeighboringSwitchPair(switchPair, "src") if (halfDifferent) result = [switchPair, halfDifferent] return result } - def flow1 = getFirstFlow(swPairs?.get(0), swPairs?.get(1)) - def flow2 = getSecondFlow(swPairs?.get(0), swPairs?.get(1), flow1) - [switchPairs: swPairs, flow1: flow1, flow2: flow2].tap(iterationData) + def flow1 = getFirstFlow(switchPairs?.get(0), switchPairs?.get(1)) + def flow2 = getSecondFlow(switchPairs?.get(0), switchPairs?.get(1), flow1) + [switchPairs: switchPairs, flow1: flow1, flow2: flow2].tap(iterationData) } } @@ -336,7 +338,7 @@ switches"() { it.flow2Src = it.flow1.destination it.flow2Dst = it.flow2.destination }].collect { iterationData -> - def switchPairs = getTopologyHelper().getAllNotNeighboringSwitchPairs().inject(null) { result, switchPair -> + def switchPairs = getSwitchPairs().all().nonNeighbouring().getSwitchPairs().inject(null) { result, switchPair -> if (result) return result def halfDifferent = getHalfDifferentNotNeighboringSwitchPair(switchPair, "src") if (halfDifferent) result = [switchPair, halfDifferent] @@ -370,8 +372,8 @@ switches"() { validateFlows(flow1, flow2) and: "Switch validation doesn't show any missing/excess rules and meters" - validateSwitches(switchPairs[0]) - validateSwitches(switchPairs[1]) + validateSwitches(swPairs[0]) + validateSwitches(swPairs[1]) cleanup: "Delete flows" [flow1, flow2].each { it && flowHelper.deleteFlow(it.id) } @@ -380,14 +382,14 @@ switches"() { endpointsPart << ["vlans", "ports", "switches"] proprtyName << ["vlanId", "portNumber", "datapath"] description = "src1 <-> dst2, dst1 <-> src2" - switchPairs = getTopologyHelper().getAllNotNeighboringSwitchPairs().inject(null) { result, switchPair -> + swPairs = switchPairs.all().nonNeighbouring().getSwitchPairs().inject(null) { result, switchPair -> if (result) return result def halfDifferent = getHalfDifferentNotNeighboringSwitchPair(switchPair, "dst") if (halfDifferent) result = [switchPair, halfDifferent] return result } - flow1 = getFirstFlow(switchPairs?.get(0), switchPairs?.get(1)) - flow2 = getSecondFlow(switchPairs?.get(0), switchPairs?.get(1), flow1) + flow1 = getFirstFlow(swPairs?.get(0), swPairs?.get(1)) + flow2 = getSecondFlow(swPairs?.get(0), swPairs?.get(1), flow1) flow1Src = changePropertyValue(flow1.source, proprtyName, flow2.destination."$proprtyName") flow1Dst = changePropertyValue(flow1.destination, proprtyName, flow2.source."$proprtyName") flow2Src = changePropertyValue(flow2.source, proprtyName, flow1.destination."$proprtyName") @@ -425,7 +427,7 @@ switches"() { endpointsPart << ["vlans", "ports", "switches"] proprtyName << ["vlanId", "portNumber", "datapath"] description = "src1 <-> dst2, dst1 <-> src2" - flow1SwitchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + flow1SwitchPair = switchPairs.all().nonNeighbouring().random() flow2SwitchPair = getDifferentNotNeighboringSwitchPair(flow1SwitchPair) flow1 = getFirstFlow(flow1SwitchPair, flow2SwitchPair) flow2 = getSecondFlow(flow1SwitchPair, flow2SwitchPair, flow1) @@ -503,7 +505,7 @@ switches"() { it.flow2Src = it.flow1.destination it.flow2Dst = it.flow2.destination }].collect { iterationData -> - def flow1SwitchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def flow1SwitchPair = switchPairs.all().nonNeighbouring().random() def flow2SwitchPair = getDifferentNotNeighboringSwitchPair(flow1SwitchPair) def flow1 = getFirstFlow(flow1SwitchPair, flow2SwitchPair) [flow1SwitchPair: flow1SwitchPair, flow2SwitchPair: flow2SwitchPair, flow1: flow1].tap(iterationData) @@ -650,7 +652,7 @@ switches"() { flow2Src = changePropertyValue(flow2.source, "portNumber", flow1.destination.portNumber) flow2Dst = flow2.destination }].collect { iterationData -> - def flow1SwitchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def flow1SwitchPair = switchPairs.all().nonNeighbouring().random() def flow2SwitchPair = getDifferentNotNeighboringSwitchPair(flow1SwitchPair) def flow1 = getFirstFlow(flow1SwitchPair, flow2SwitchPair) [flow1SwitchPair: flow1SwitchPair, flow2SwitchPair: flow2SwitchPair, flow1: flow1].tap(iterationData) @@ -703,7 +705,7 @@ switches"() { flow2Src = changePropertyValue(flow2.source, "portNumber", flow1.source.portNumber) flow2Dst = flow1.destination }].collect { iterationData -> - def flow1SwitchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def flow1SwitchPair = switchPairs.all().nonNeighbouring().random() def flow2SwitchPair = getDifferentNotNeighboringSwitchPair(flow1SwitchPair) def flow1 = getFirstFlow(flow1SwitchPair, flow2SwitchPair) def flow2 = getSecondFlow(flow1SwitchPair, flow2SwitchPair, flow1) @@ -718,7 +720,7 @@ switches"() { def "Unable to swap ports for two flows (port is occupied by ISL on src switch)"() { given: "Two active flows" def islPort - def swPair = topologyHelper.switchPairs.find { + def swPair = switchPairs.all().getSwitchPairs().find { def busyPorts = topology.getBusyPortsForSwitch(it.src) islPort = topology.getAllowedPortsForSwitch(it.dst).find { it in busyPorts } } @@ -754,7 +756,7 @@ switches"() { @Tags(ISL_RECOVER_ON_FAIL) def "Able to swap endpoints for two flows when all bandwidth on ISL is consumed"() { setup: "Create two flows with different source and the same destination switches" - List switchPairs = topologyHelper.allNeighboringSwitchPairs.inject(null) { result, switchPair -> + List switchPairs = getSwitchPairs().all().neighbouring().getSwitchPairs().inject(null) { result, switchPair -> if (result) return result def halfDifferent = getHalfDifferentNeighboringSwitchPair(switchPair, "dst") if (halfDifferent) result = [switchPair, halfDifferent] @@ -848,7 +850,7 @@ switches"() { @Tags(ISL_RECOVER_ON_FAIL) def "Unable to swap endpoints for two flows when not enough bandwidth on ISL"() { setup: "Create two flows with different source and the same destination switches" - List switchPairs = topologyHelper.allNeighboringSwitchPairs.inject(null) { result, switchPair -> + List switchPairs = getSwitchPairs().all().neighbouring().getSwitchPairs().inject(null) { result, switchPair -> if (result) return result def halfDifferent = getHalfDifferentNeighboringSwitchPair(switchPair, "dst") if (halfDifferent) result = [switchPair, halfDifferent] @@ -938,7 +940,7 @@ switches"() { @Tags([LOW_PRIORITY, ISL_RECOVER_ON_FAIL]) def "Able to swap endpoints for two flows when not enough bandwidth on ISL and ignore_bandwidth=true"() { setup: "Create two flows with different source and the same destination switches" - List switchPairs = topologyHelper.allNeighboringSwitchPairs.inject(null) { result, switchPair -> + List switchPairs = getSwitchPairs().all().neighbouring().getSwitchPairs().inject(null) { result, switchPair -> if (result) return result def halfDifferent = getHalfDifferentNeighboringSwitchPair(switchPair, "dst") if (halfDifferent) result = [switchPair, halfDifferent] @@ -1029,11 +1031,10 @@ switches"() { database.resetCosts(topology.isls) } - @Ignore("https://github.com/telstra/open-kilda/issues/3770") @Tags(ISL_RECOVER_ON_FAIL) def "Unable to swap endpoints for two flows when one of them is inactive"() { setup: "Create two flows with different source and the same destination switches" - List switchPairs = topologyHelper.allNeighboringSwitchPairs.inject(null) { result, switchPair -> + def switchPairs = getSwitchPairs().all().neighbouring().getSwitchPairs().inject(null) { result, switchPair -> if (result) return result def halfDifferent = getHalfDifferentNeighboringSwitchPair(switchPair, "dst") if (halfDifferent) result = [switchPair, halfDifferent] @@ -1078,35 +1079,26 @@ switches"() { then: "An error is received (500 code)" def exc = thrown(HttpServerErrorException) - exc.rawStatusCode == 500 - def error = exc.responseBodyAsString.to(MessageError) - error.errorMessage == "Could not swap endpoints" - error.errorDescription.contains("Not enough bandwidth or no path found") + new FlowEndpointsNotSwappedExpectedError(~/Not enough bandwidth or no path found/).matches(exc) and: "All involved switches are valid" Wrappers.wait(RULES_INSTALLATION_TIME) { - involvedSwIds.each { swId -> - verifyAll(northbound.validateSwitch(swId)) { - rules.missing.empty - rules.excess.empty - rules.misconfigured.empty - meters.missing.empty - meters.excess.empty - meters.misconfigured.empty - } - } + assert switchHelper.validate(involvedSwIds).isEmpty() } Boolean isTestCompleted = true cleanup: "Restore topology and delete flows" [flow1, flow2].each { it && flowHelper.deleteFlow(it.id) } + //https://github.com/telstra/open-kilda/issues/3770 + switchHelper.synchronize(involvedSwIds) broughtDownPorts.every { antiflap.portUp(it.switchId, it.portNo) } Wrappers.wait(discoveryInterval + WAIT_OFFSET) { northbound.getAllLinks().each { assert it.state != IslChangeType.FAILED } } if (!isTestCompleted) { - [flow1SwitchPair.src.dpId, flow1SwitchPair.dst.dpId, flow2SwitchPair.src.dpId, flow2SwitchPair.dst.dpId] - .unique().each { northbound.synchronizeSwitch(it, true) } + switchHelper.synchronize([flow1SwitchPair.src.dpId, flow1SwitchPair.dst.dpId, + flow2SwitchPair.src.dpId, flow2SwitchPair.dst.dpId] + .unique()) } database.resetCosts(topology.isls) } @@ -1172,7 +1164,7 @@ switches"() { flow2Src = flow1.destination flow2Dst = flow2.destination }].collect { iterationData -> - def flow1SwitchPair = getTopologyHelper().getAllSwitchPairs().nonNeighbouring().random() + def flow1SwitchPair = switchPairs.all().nonNeighbouring().random() def flow2SwitchPair = getDifferentNotNeighboringSwitchPair(flow1SwitchPair) def flow1 = getFlowHelper().randomFlow(flow1SwitchPair) def flow2 = getFlowHelper().randomFlow(flow2SwitchPair, false, [flow1]).tap { @@ -1189,21 +1181,15 @@ switches"() { def "A protected flow with swapped endpoint allows traffic on main and protected paths"() { given: "Two protected flows with different source and destination switches" - def tgSwitches = topology.getActiveTraffGens()*.getSwitchConnected() - assumeTrue(tgSwitches.size() > 1, "Not enough traffgen switches found") - SwitchPair flow2SwitchPair = null - SwitchPair flow1SwitchPair = topologyHelper.getAllNeighboringSwitchPairs().find { firstPair -> - def firstOk = !(firstPair.src in tgSwitches) && firstPair.dst in tgSwitches - flow2SwitchPair = topologyHelper.getAllNeighboringSwitchPairs().find { secondPair -> - !(secondPair.src in [firstPair.src, firstPair.dst]) && - !(secondPair.dst in [firstPair.src, firstPair.dst]) && - secondPair.src in tgSwitches && !(secondPair.dst in tgSwitches) - } - firstOk && flow2SwitchPair - } - assumeTrue(flow1SwitchPair.asBoolean() && flow2SwitchPair.asBoolean(), - "Required switch pairs not found in given topology") - + def flow1SwitchPair = switchPairs.all(false) + .neighbouring() + .withAtLeastNTraffgensOnDestination(1) + .random() + def flow2SwitchPair = switchPairs.all(false) + .neighbouring() + .excludeSwitches([flow1SwitchPair.getSrc(), flow1SwitchPair.getDst()]) + .withAtLeastNTraffgensOnSource(1) + .random() def flow1 = flowHelper.randomFlow(flow1SwitchPair) def flow2 = flowHelper.randomFlow(flow2SwitchPair) @@ -1305,9 +1291,7 @@ switches"() { flow2Src = flow2.source flow2Dst = flow1.destination }].collect { iterationData -> - def switchPair = getTopologyHelper().getAllNeighboringSwitchPairs().find { - [it.src, it.dst].every { switchHelper.isVxlanEnabled(it.dpId) } - } + def switchPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().random() def flow1 = getFirstFlow(switchPair, switchPair) def flow2 = getSecondFlow(switchPair, switchPair, flow1) [switchPair: switchPair, flow1: flow1, flow2: flow2].tap(iterationData) @@ -1363,7 +1347,7 @@ switches"() { flow2Src = flow2.source flow2Dst = flow1.destination }].collect { iterationData -> - def switchPair = getTopologyHelper().getAllNeighboringSwitchPairs().shuffled().first() + def switchPair = switchPairs.all().neighbouring().random() def flow1 = getFirstFlow(switchPair, switchPair).tap { source.innerVlanId = 300 destination.innerVlanId = 400 @@ -1381,13 +1365,12 @@ switches"() { def "System reverts both flows if fails during rule installation when swapping endpoints"() { given: "Two flows with different src switches and same dst" - def swPair1 - def swPair2 = topologyHelper.switchPairs.find { second -> - swPair1 = topologyHelper.switchPairs.find { first -> - first.src != second.src && first.dst == second.dst - } - } - assumeTrue(swPair1 && swPair2, "Unable to find 2 switch pairs with different src and same dst switches") + def swPair1 = switchPairs.all().random() + def swPair2 = switchPairs.all() + .excludeSwitches([swPair1.getSrc()]) + .includeSourceSwitch(swPair1.getDst()) + .random() + .getReversed() def flow1 = flowHelperV2.randomFlow(swPair1).tap { it.source.portNumber = getFreePort(swPair1.src, [swPair2.src]) } @@ -1464,19 +1447,11 @@ switches"() { def "Able to swap endpoints for a flow with flowLoop"() { setup: "Create two flows with the same src and different dst switches" - def tgSwitchIds = topology.getActiveTraffGens()*.switchConnected*.dpId - assumeTrue(tgSwitchIds.size() > 1, "Not enough traffgen switches found") - SwitchPair flow2SwitchPair = null - SwitchPair flow1SwitchPair = topologyHelper.getAllNeighboringSwitchPairs().find { firstPair -> - def firstOk = firstPair.src.dpId in tgSwitchIds && firstPair.dst.dpId in tgSwitchIds - flow2SwitchPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { secondPair -> - secondPair.src.dpId == firstPair.src.dpId && secondPair.dst.dpId != firstPair.dst.dpId - } - firstOk && flow2SwitchPair - } - assumeTrue(flow1SwitchPair.asBoolean() && flow2SwitchPair.asBoolean(), - "Required switch pairs not found in given topology") - + def flow1SwitchPair = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random().getReversed() + def flow2SwitchPair = switchPairs.all().neighbouring() + .includeSourceSwitch(flow1SwitchPair.getSrc()) + .excludeDestinationSwitches([flow1SwitchPair.getDst()]) + .random() def flow1 = flowHelper.randomFlow(flow1SwitchPair) def flow2 = flowHelper.randomFlow(flow2SwitchPair, true, [flow1]) @@ -1743,14 +1718,14 @@ switches"() { def getHalfDifferentNotNeighboringSwitchPair(switchPairToAvoid, equalEndpoint) { def differentEndpoint = (equalEndpoint == "src" ? "dst" : "src") - topologyHelper.getAllNotNeighboringSwitchPairs().find { + switchPairs.all().nonNeighbouring().getSwitchPairs().find { it."$equalEndpoint" == switchPairToAvoid."$equalEndpoint" && it."$differentEndpoint" != switchPairToAvoid."$differentEndpoint" } } def getDifferentNotNeighboringSwitchPair(switchPairToAvoid) { - topologyHelper.getAllNotNeighboringSwitchPairs().find { + switchPairs.all().nonNeighbouring().getSwitchPairs().find { !(it.src in [switchPairToAvoid.src, switchPairToAvoid.dst]) && !(it.dst in [switchPairToAvoid.src, switchPairToAvoid.dst]) } @@ -1762,7 +1737,7 @@ switches"() { def getHalfDifferentNeighboringSwitchPair(switchPairToAvoid, equalEndpoint) { def differentEndpoint = (equalEndpoint == "src" ? "dst" : "src") - topologyHelper.getAllNeighboringSwitchPairs().find { + switchPairs.all().neighbouring().getSwitchPairs().find { it."$equalEndpoint" == switchPairToAvoid."$equalEndpoint" && it."$differentEndpoint" != switchPairToAvoid."$differentEndpoint" } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ThrottlingRerouteSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ThrottlingRerouteSpec.groovy index daeafd962ed..301f550bbbc 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ThrottlingRerouteSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/ThrottlingRerouteSpec.groovy @@ -43,15 +43,14 @@ class ThrottlingRerouteSpec extends HealthCheckSpecification { int rerouteHardTimeout @Tags([SMOKE, ISL_RECOVER_ON_FAIL]) - // unignored. Tests needs supervision next builds def "Reroute is not performed while new reroutes are being issued"() { given: "Multiple flows that can be rerouted independently (use short unique paths)" /* Here we will pick only short flows that consist of 2 switches, so that we can maximize amount of unique flows found*/ - def switchPairs = topologyHelper.getAllNeighboringSwitchPairs() + def swPairs = switchPairs.all(false).neighbouring().getSwitchPairs() - assumeTrue(switchPairs.size() > 3, "Topology is too small to run this test") - def flows = switchPairs.take(5).collect { switchPair -> + assumeTrue(swPairs.size() > 3, "Topology is too small to run this test") + def flows = swPairs.take(5).collect { switchPair -> def flow = flowHelperV2.randomFlow(switchPair) flowHelperV2.addFlow(flow) flow @@ -110,7 +109,7 @@ class ThrottlingRerouteSpec extends HealthCheckSpecification { given: "Multiple flows that can be rerouted independently (use short unique paths)" /* Here we will pick only short flows that consist of 2 switches, so that we can maximize amount of unique flows found*/ - def switchPairs = topologyHelper.getAllNeighboringSwitchPairs() + def switchPairs = switchPairs.all().neighbouring().getSwitchPairs() /*due to port anti-flap we cannot continuously quickly reroute one single flow until we reach hardTimeout, thus we need certain amount of flows to continuously provide reroute triggers for them in a loop. diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/VxlanFlowSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/VxlanFlowSpec.groovy index 3a23aa8d862..d6c89e59baa 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/VxlanFlowSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/VxlanFlowSpec.groovy @@ -206,11 +206,7 @@ class VxlanFlowSpec extends HealthCheckSpecification { def "Able to CRUD a pinned flow with 'VXLAN' encapsulation"() { when: "Create a flow" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { swP -> - [swP.src, swP.dst].every { sw -> switchHelper.isVxlanEnabled(sw.dpId) } - } - assumeTrue(switchPair as boolean, "Unable to find required switches in topology") - + def switchPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().random() def flow = flowHelperV2.randomFlow(switchPair) flow.encapsulationType = VXLAN flow.pinned = true @@ -237,12 +233,10 @@ class VxlanFlowSpec extends HealthCheckSpecification { def "Able to CRUD a vxlan flow with protected path"() { given: "Two active VXLAN supported switches with two available path at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { swP -> - [swP.src, swP.dst].every { sw -> switchHelper.isVxlanEnabled(sw.dpId) } && swP.paths.unique(false) { - a, b -> a.intersect(b) == [] ? 1 : 0 - }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") - + def switchPair = switchPairs.all().neighbouring() + .withBothSwitchesVxLanEnabled() + .withAtLeastNNonOverlappingPaths(2) + .random() def availablePaths = switchPair.paths.findAll { path -> pathHelper.getInvolvedSwitches(path).every { switchHelper.isVxlanEnabled(it.dpId) } } @@ -356,14 +350,10 @@ class VxlanFlowSpec extends HealthCheckSpecification { def "System allows tagged traffic via default flow(0<->0) with 'VXLAN' encapsulation"() { // we can't test (0<->20, 20<->0) because iperf is not able to establish a connection given: "Two active VXLAN supported switches connected to traffgen" - def allTraffgenSwitchIds = topology.activeTraffGens*.switchConnected.findAll { - switchHelper.isVxlanEnabled(it.dpId) - }*.dpId ?: assumeTrue(false, -"Should be at least two active traffgens connected to VXLAN supported switches") - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - allTraffgenSwitchIds.contains(it.src.dpId) && allTraffgenSwitchIds.contains(it.dst.dpId) - } ?: assumeTrue(false, "Unable to find required switches in topology") - + def switchPair = switchPairs.all().neighbouring() + .withBothSwitchesVxLanEnabled() + .withTraffgensOnBothEnds() + .random() when: "Create a default flow" def defaultFlow = flowHelperV2.randomFlow(switchPair) defaultFlow.source.vlanId = 0 @@ -392,7 +382,7 @@ class VxlanFlowSpec extends HealthCheckSpecification { def "Unable to create a VXLAN flow when src and dst switches do not support it"() { given: "Src and dst switches do not support VXLAN" - def switchPair = topologyHelper.switchPairs.first() + def switchPair = switchPairs.all().random() Map initProps = [switchPair.src, switchPair.dst].collectEntries { [(it): switchHelper.getCachedSwProps(it.dpId)] } @@ -443,7 +433,7 @@ class VxlanFlowSpec extends HealthCheckSpecification { given: "Shortest path transit switch does not support VXLAN and alt paths with VXLAN are available" List noVxlanPath Switch noVxlanSw - def switchPair = topologyHelper.switchPairs.find { + def switchPair = switchPairs.all().getSwitchPairs().find { noVxlanPath = it.paths.find { def involvedSwitches = pathHelper.getInvolvedSwitches(it) noVxlanSw = involvedSwitches[1] @@ -486,18 +476,11 @@ class VxlanFlowSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY, TOPOLOGY_DEPENDENT]) def "Unable to create a vxlan flow when dst switch does not support it"() { given: "VXLAN supported and not supported switches" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - switchHelper.isVxlanEnabled(it.src.dpId) - } - assumeTrue(switchPair as boolean, "Unable to find required switches in topology") - def isVxlanEnabledOnDstSw = switchHelper.isVxlanEnabled(switchPair.dst.dpId) - def originDstSwProps - if (isVxlanEnabledOnDstSw) { - originDstSwProps = switchHelper.getCachedSwProps(switchPair.dst.dpId) - switchHelper.updateSwitchProperties(switchPair.dst, originDstSwProps.jacksonCopy().tap { - it.supportedTransitEncapsulation = [FlowEncapsulationType.TRANSIT_VLAN.toString()] - }) - } + def switchPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().random() + def originDstSwProps = switchHelper.getCachedSwProps(switchPair.dst.dpId) + switchHelper.updateSwitchProperties(switchPair.dst, originDstSwProps.jacksonCopy().tap { + it.supportedTransitEncapsulation = [FlowEncapsulationType.TRANSIT_VLAN.toString()] + }) def dstSupportedEncapsulationTypes = northbound.getSwitchProperties(switchPair.dst.dpId) .supportedTransitEncapsulation.collect { it.toUpperCase() } @@ -515,7 +498,7 @@ class VxlanFlowSpec extends HealthCheckSpecification { cleanup: !exc && flowHelperV2.deleteFlow(flow.flowId) - isVxlanEnabledOnDstSw && switchHelper.updateSwitchProperties(switchPair.dst, originDstSwProps) + switchHelper.updateSwitchProperties(switchPair.dst, originDstSwProps) } def "System allows to create/update encapsulation type for a one-switch flow\ @@ -613,10 +596,7 @@ class VxlanFlowSpec extends HealthCheckSpecification { * Get minimum amount of switchPairs that will use every unique legal switch as src or dst at least once */ List getUniqueVxlanSwitchPairs() { - def vxlanEnabledSwitches = topology.activeSwitches.findAll { switchHelper.isVxlanEnabled(it.dpId) } - def vxlanSwitchPairs = topologyHelper.getSwitchPairs().findAll { swPair -> - swPair.paths.find { pathHelper.getInvolvedSwitches(it).every { it in vxlanEnabledSwitches } } - } + def vxlanSwitchPairs = switchPairs.all().withBothSwitchesVxLanEnabled().getSwitchPairs() def switchesToPick = vxlanSwitchPairs.collectMany { [it.src, it.dst] } .unique { it.nbFormat().hardware + it.nbFormat().software } return vxlanSwitchPairs.inject([]) { r, switchPair -> diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/yflows/YFlowCreateSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/yflows/YFlowCreateSpec.groovy index 26af88126ea..87f995a4949 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/yflows/YFlowCreateSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/yflows/YFlowCreateSpec.groovy @@ -5,6 +5,7 @@ import org.openkilda.functionaltests.error.yflow.YFlowNotCreatedWithConflictExpe import static groovyx.gpars.GParsPool.withPool import static org.junit.jupiter.api.Assumptions.assumeTrue +import static org.openkilda.functionaltests.extension.tags.Tag.HARDWARE import static org.openkilda.functionaltests.extension.tags.Tag.LOW_PRIORITY import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT import static org.openkilda.functionaltests.helpers.FlowHistoryConstants.CREATE_SUCCESS @@ -408,7 +409,7 @@ source: switchId="${flow.sharedEndpoint.switchId}" port=${flow.sharedEndpoint.po } } - @Tags([TOPOLOGY_DEPENDENT]) + @Tags([HARDWARE]) def "System forbids to create a y-flow with conflict: shared endpoint port is inside a LAG group"() { given: "A LAG port" def swT = topologyHelper.switchTriplets.find { it.shared.features.contains(SwitchFeature.LAG) } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/yflows/YFlowDiversitySpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/yflows/YFlowDiversitySpec.groovy index cddae187329..e4f85edc709 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/yflows/YFlowDiversitySpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/yflows/YFlowDiversitySpec.groovy @@ -165,10 +165,7 @@ class YFlowDiversitySpec extends HealthCheckSpecification { def "Able to create y-flow with one switch sub flow and diverse with simple multiSwitch flow"() { given: "Two switches with two not overlapping paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.collect { pathHelper.getInvolvedIsls(it) } - .unique { a, b -> a.intersect(b) ? 0 : 1 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() and: "Simple multiSwitch flow" def flow = flowHelperV2.randomFlow(switchPair.src, switchPair.dst, false) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/IslMinPortSpeedSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/IslMinPortSpeedSpec.groovy index 69de3b268aa..65bc4375677 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/IslMinPortSpeedSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/IslMinPortSpeedSpec.groovy @@ -1,6 +1,7 @@ package org.openkilda.functionaltests.spec.links import static org.junit.jupiter.api.Assumptions.assumeTrue +import static org.openkilda.functionaltests.extension.tags.Tag.HARDWARE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT import static org.openkilda.messaging.info.event.IslChangeType.DISCOVERED @@ -18,7 +19,7 @@ Sometimes an ISL have different port speed on its edges. In that case, we need to set ISL capacity and all bandwidth parameters according to minimal speed value. Eg. 10G on one side, and 1G on another side, the ISL should have a 1G capacity.""") class IslMinPortSpeedSpec extends HealthCheckSpecification { - @Tags([SMOKE, TOPOLOGY_DEPENDENT]) + @Tags([SMOKE, HARDWARE]) def "System sets min port speed for isl capacity"() { given: "Two ports with different port speed" def isl = topology.islsForActiveSwitches.find { diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/IslReplugSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/IslReplugSpec.groovy index ee1b1abeee0..be10c013302 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/IslReplugSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/IslReplugSpec.groovy @@ -2,6 +2,7 @@ package org.openkilda.functionaltests.spec.links import static org.junit.Assume.assumeNotNull import static org.junit.jupiter.api.Assumptions.assumeTrue +import static org.openkilda.functionaltests.extension.tags.Tag.HARDWARE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT import static org.openkilda.functionaltests.model.stats.SwitchStatsMetric.FLOW_SYSTEM_PACKETS @@ -30,6 +31,7 @@ class IslReplugSpec extends HealthCheckSpecification { @Autowired @Shared SwitchStats switchStats + @Tags(HARDWARE) def "Round-trip ISL status changes to MOVED when replugging it into another switch"() { given: "A connected a-switch link, round-trip-enabled" and: "A non-connected a-switch link with round-trip support" @@ -230,7 +232,8 @@ class IslReplugSpec extends HealthCheckSpecification { def expectedIsl = islUtils.replug(islToPlug, true, islToPlugInto, true, true) then: "The potential self-loop ISL is not present in the list of ISLs (wait for discovery interval)" - Wrappers.wait(discoveryInterval + WAIT_OFFSET) { + sleep(discoveryInterval * 1000) + Wrappers.wait(WAIT_OFFSET) { def allLinks = northbound.getAllLinks() !islUtils.getIslInfo(allLinks, expectedIsl).present !islUtils.getIslInfo(allLinks, expectedIsl.reversed).present diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/LinkMaintenanceSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/LinkMaintenanceSpec.groovy index f8262be3177..388ddcdaff3 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/LinkMaintenanceSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/LinkMaintenanceSpec.groovy @@ -50,8 +50,7 @@ class LinkMaintenanceSpec extends HealthCheckSpecification { def "Flows can be evacuated (rerouted) from a particular link when setting maintenance mode for it"() { given: "Two active not neighboring switches with two possible paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNPaths(2).random() and: "Create a couple of flows going through these switches" def flow1 = flowHelperV2.randomFlow(switchPair) @@ -99,8 +98,7 @@ class LinkMaintenanceSpec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "Flows are rerouted to a path with link under maintenance when there are no other paths available"() { given: "Two active not neighboring switches with two possible paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNPaths(2).random() and: "Create a couple of flows going through these switches" def flow1 = flowHelperV2.randomFlow(switchPair) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/LinkSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/LinkSpec.groovy index 67ab39b4da6..f5af4654e8d 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/LinkSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/LinkSpec.groovy @@ -414,8 +414,7 @@ class LinkSpec extends HealthCheckSpecification { def "Reroute all flows going through a particular link"() { given: "Two active not neighboring switches with two possible paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { it.paths.size() > 1 } ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNPaths(2).random() and: "Make the first path more preferable than others by setting corresponding link props" switchPair.paths[1..-1].each { pathHelper.makePathMorePreferable(switchPair.paths.first(), it) } @@ -758,9 +757,7 @@ class LinkSpec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "Able to delete an active link with flowPath if using force delete"() { given: "Two active neighboring switches and two possible paths at least" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() and: "An active link with flow on it" def flow = flowHelperV2.randomFlow(switchPair) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/UnstableIslSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/UnstableIslSpec.groovy index 9d2bcd987cb..fe54e77eb06 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/UnstableIslSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/links/UnstableIslSpec.groovy @@ -105,9 +105,7 @@ class UnstableIslSpec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "ISL is marked as 'unstable' after port down and system takes it into account during flow creation"() { given: "Two active neighboring switches with two parallel links" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.findAll { it.size() == 2 }.size() > 1 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNIslsBetweenNeighbouringSwitches(2).random() and: "Two possible paths for further manipulation with them" def firstPath = switchPair.paths.min { it.size() } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/network/PathComputationSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/network/PathComputationSpec.groovy index a76b56cbbea..cf401fb1327 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/network/PathComputationSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/network/PathComputationSpec.groovy @@ -23,7 +23,7 @@ class PathComputationSpec extends HealthCheckSpecification { new KildaConfigurationDto(pathComputationStrategy: PathComputationStrategy.COST)) and: "Switch pair with two paths at least" - def swPair = topologyHelper.switchPairs.find { it.paths.size() >= 2 } + def swPair = switchPairs.all().withAtLeastNPaths(2).random() and: "Update paths so that one path has minimal total latency and the other has minimal total cost" def costEffectivePath = swPair.paths[0] @@ -78,7 +78,7 @@ class PathComputationSpec extends HealthCheckSpecification { def "Flow path computation strategy can be updated from LATENCY to COST"() { given: "Switch pair with two paths at least" - def swPair = topologyHelper.switchPairs.find { it.paths.size() >= 2 } + def swPair = switchPairs.all().withAtLeastNPaths(2).random() and: "Update paths so that one path has minimal total latency and the other has minimal total cost" def costEffectivePath = swPair.paths[0] @@ -111,7 +111,7 @@ class PathComputationSpec extends HealthCheckSpecification { def "Target flow path computation strategy is not applied immediately in case flow was updated partially"() { given: "Switch pair with two paths at least" - def swPair = topologyHelper.switchPairs.find { it.paths.size() >= 2 } + def swPair = switchPairs.all().withAtLeastNPaths(2).random() and: "A flow with cost strategy" def latencyStrategy = PathComputationStrategy.LATENCY.toString().toLowerCase() @@ -149,7 +149,7 @@ class PathComputationSpec extends HealthCheckSpecification { def "Target path computation strategy is applied after updating/rerouting a flow"() { given: "Switch pair with two paths at least" - def swPair = topologyHelper.switchPairs.find { it.paths.size() >= 2 } + def swPair = switchPairs.all().withAtLeastNPaths(2).random() and: "A flow with cost strategy" def latencyStrategy = PathComputationStrategy.LATENCY.toString().toLowerCase() diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/server42/Server42FlowRttSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/server42/Server42FlowRttSpec.groovy index 7561eebf3a5..271f444f3b0 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/server42/Server42FlowRttSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/server42/Server42FlowRttSpec.groovy @@ -1,5 +1,9 @@ package org.openkilda.functionaltests.spec.server42 +import org.openkilda.functionaltests.extension.tags.IterationTag +import org.openkilda.functionaltests.helpers.model.SwitchPairs +import org.openkilda.functionaltests.model.switches.Manufacturer + import static groovyx.gpars.GParsPool.withPool import static java.util.concurrent.TimeUnit.SECONDS import static org.assertj.core.api.Assertions.assertThat @@ -13,6 +17,8 @@ import static org.openkilda.functionaltests.model.stats.Direction.REVERSE import static org.openkilda.functionaltests.model.stats.FlowStatsMetric.FLOW_RTT import static org.openkilda.functionaltests.model.stats.Origin.FLOW_MONITORING import static org.openkilda.functionaltests.model.stats.Origin.SERVER_42 +import static org.openkilda.functionaltests.model.switches.Manufacturer.WB5164 +import static org.openkilda.model.FlowEncapsulationType.VXLAN import static org.openkilda.model.SwitchFeature.KILDA_OVS_PUSH_POP_MATCH_VXLAN import static org.openkilda.model.cookie.Cookie.SERVER_42_FLOW_RTT_OUTPUT_VLAN_COOKIE import static org.openkilda.model.cookie.Cookie.SERVER_42_FLOW_RTT_OUTPUT_VXLAN_COOKIE @@ -29,9 +35,7 @@ import org.openkilda.functionaltests.helpers.model.SwitchPair import org.openkilda.functionaltests.model.stats.FlowStats import org.openkilda.messaging.model.system.FeatureTogglesDto import org.openkilda.messaging.payload.flow.FlowState -import org.openkilda.model.FlowEncapsulationType import org.openkilda.model.SwitchFeature -import org.openkilda.model.SwitchId import org.openkilda.model.cookie.Cookie import org.openkilda.model.cookie.CookieBase.CookieType import org.openkilda.northbound.dto.v2.flows.FlowPatchEndpoint @@ -69,24 +73,23 @@ class Server42FlowRttSpec extends HealthCheckSpecification { @Value('${flow.sla.check.interval.seconds}') Integer flowSlaCheckIntervalSeconds - def "Create a #data.flowDescription flow with server42 Rtt feature and check datapoints in tsdb"() { + @Tags(TOPOLOGY_DEPENDENT) + @IterationTag(tags = [HARDWARE], iterationNameRegex = /(NS|WB)/) + def "Create a #flowDescription flow with server42 Rtt feature and check datapoints in tsdb"() { given: "Two active switches, src has server42 connected" - def server42switches = topology.getActiveServer42Switches(); - assumeTrue((server42switches.size() > 0), "Unable to find active server42") - def server42switchesDpIds = server42switches*.dpId; - def switchPair = data.switchPair(server42switchesDpIds) - assumeTrue(switchPair != null, "Was not able to find a switch with a server42 connected") + def switchPair = switchPairFilter(switchPairs.all().withBothSwitchesConnectedToServer42()).random() when: "Set server42FlowRtt toggle to true" def flowRttFeatureStartState = changeFlowRttToggle(true) and: "server42FlowRtt is enabled on src and dst switches" def server42Switch = switchPair.src - def initialSwitchRtt = [server42Switch, switchPair.dst].collectEntries { [it, changeFlowRttSwitch(it, true)] } + def initialSwitchRtt = [server42Switch, switchPair.dst] + .collectEntries { [it, changeFlowRttSwitch(it, true)] } and: "Create a flow" def flow = flowHelperV2.randomFlow(switchPair) - flow.tap(data.flowTap) + flow.tap(flowTap) flowHelperV2.addFlow(flow) then: "Check if stats for forward are available" @@ -98,52 +101,36 @@ class Server42FlowRttSpec extends HealthCheckSpecification { revertToOrigin([flow], flowRttFeatureStartState, initialSwitchRtt) where: - data << [[ - flowDescription: "default flow", - switchPair : { List switchIds -> getSwPairConnectedToS42ForSimpleFlow(switchIds) }, - flowTap : { FlowRequestV2 fl -> - fl.source.vlanId = 0 - fl.destination.vlanId = 0 - } - ], - [ - flowDescription: "protected flow", - switchPair : { List switchIds -> getSwPairConnectedToS42ForProtectedFlow(switchIds) }, - flowTap : { FlowRequestV2 fl -> fl.allocateProtectedPath = true } - ], - [ - flowDescription: "vxlan flow on NS switch", - switchPair : { List switchIds -> - getSwPairConnectedToS42ForVxlanFlowOnNonWbSwitch(switchIds) }, - flowTap : { FlowRequestV2 fl -> fl.encapsulationType = FlowEncapsulationType.VXLAN } - ], - [ - flowDescription: "qinq flow", - switchPair : { List switchIds -> getSwPairConnectedToS42ForQinQ(switchIds) }, - flowTap : { FlowRequestV2 fl -> - fl.source.vlanId = 10 - fl.source.innerVlanId = 100 - fl.destination.vlanId = 20 - fl.destination.innerVlanId = 200 - } - ], - [ - flowDescription: "vxlan flow on WB switch", - switchPair : { List switchIds -> - getSwPairConnectedToS42ForVxlanFlowOnWbSwitch(switchIds) }, - flowTap : { FlowRequestV2 fl -> fl.encapsulationType = FlowEncapsulationType.VXLAN } - ], - ] + flowDescription | switchPairFilter | flowTap + "default flow" | { SwitchPairs swPairs -> swPairs } | { FlowRequestV2 fl -> + fl.source.vlanId = 0 + fl.destination.vlanId = 0 + } + "protected flow" | { SwitchPairs swPairs -> swPairs + .withAtLeastNNonOverlappingPaths(2)}| { FlowRequestV2 fl -> + fl.allocateProtectedPath = true } + "vxlan flow on NS switch" | { SwitchPairs swPairs -> swPairs + .withBothSwitchesVxLanEnabled() + .withSourceSwitchNotManufacturedBy(WB5164) + } | { FlowRequestV2 fl -> + fl.encapsulationType = VXLAN } + "qinq flow" | { SwitchPairs swPairs -> swPairs } | { FlowRequestV2 fl -> + fl.source.vlanId = 10 + fl.source.innerVlanId = 100 + fl.destination.vlanId = 20 + fl.destination.innerVlanId = 200 + } + "vxlan flow on WB switch" | { SwitchPairs swPairs -> swPairs + .withBothSwitchesVxLanEnabled() + .withSourceSwitchManufacturedBy(WB5164) + } | { FlowRequestV2 fl -> + fl.encapsulationType = VXLAN } } def "Flow rtt stats are available in forward and reverse directions for new flows"() { given: "Two active switches with switch having server42" - def server42switches = topology.getActiveServer42Switches() - def server42switchesDpIds = server42switches*.dpId - SwitchPair switchPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { - [it.src, it.dst].every { it.dpId in server42switchesDpIds } - } - assumeTrue(switchPair != null, "Was not able to find a switch with a server42 connected") + SwitchPair switchPair = switchPairs.all().withBothSwitchesConnectedToServer42().random() + and: "server42FlowRtt feature toggle is set to true" def flowRttFeatureStartState = changeFlowRttToggle(true) @@ -204,12 +191,7 @@ class Server42FlowRttSpec extends HealthCheckSpecification { def "Flow rtt stats are available only if both global and switch toggles are 'on' on both endpoints"() { given: "Two active switches with having server42" - def server42switches = topology.getActiveServer42Switches() - def server42switchesDpIds = server42switches*.dpId - def switchPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { - [it.src, it.dst].every { it.dpId in server42switchesDpIds } - } - assumeTrue(switchPair != null, "Was not able to find a switch with a server42 connected") + def switchPair = switchPairs.all().withBothSwitchesConnectedToServer42().random() def statsWaitSeconds = 4 and: "server42FlowRtt toggle is turned off" @@ -306,10 +288,8 @@ class Server42FlowRttSpec extends HealthCheckSpecification { @Tags([TOPOLOGY_DEPENDENT]) def "Flow rtt stats are available if both endpoints are connected to the same server42, same pop"() { given: "Two active switches connected to the same server42 instance" - def switchPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { - it.src.prop?.server42MacAddress != null && it.src.prop?.server42MacAddress == it.dst.prop?.server42MacAddress - } - assumeTrue(switchPair != null, "Was not able to find 2 switches on the same server42") + def switchPair = switchPairs.all().withBothSwitchesConnectedToSameServer42Instance().random() + and: "server42FlowRtt feature enabled globally and on src/dst switch" def flowRttFeatureStartState = changeFlowRttToggle(true) def initialSwitchRtt = [switchPair.src, switchPair.dst].collectEntries { [it, changeFlowRttSwitch(it, true)] } @@ -362,10 +342,7 @@ class Server42FlowRttSpec extends HealthCheckSpecification { @Tags(HARDWARE) //not supported on a local env (the 'stub' service doesn't send real traffic through a switch) def "Able to synchronize a flow (install missing server42 rules)"() { given: "A switch pair connected to server42" - def server42switches = topology.getActiveServer42Switches(); - def server42switchesDpIds = server42switches*.dpId; - def switchPair = getSwPairConnectedToS42ForSimpleFlow(server42switchesDpIds) - assumeTrue(switchPair != null, "Was not able to find a switch with a server42 connected") + def switchPair = switchPairs.all().withBothSwitchesConnectedToServer42().random() //enable server42 in featureToggle and on the switches def flowRttFeatureStartState = changeFlowRttToggle(true) def server42Switch = switchPair.src @@ -438,19 +415,13 @@ class Server42FlowRttSpec extends HealthCheckSpecification { @Tags(LOW_PRIORITY) def "Able to swapEndpoint for a flow with enabled server42 on it"() { given: "Two switch pairs with different src switches and the same dst switch" - def switchIdsConnectedToS42 = topology.getActiveServer42Switches()*.dpId - SwitchPair fl2SwPair = null - SwitchPair fl1SwPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { firstSwP -> - def firstOk = - firstSwP.src.dpId in switchIdsConnectedToS42 && !switchIdsConnectedToS42.contains(firstSwP.dst.dpId) - fl2SwPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { secondSwP -> - secondSwP.dst.dpId == firstSwP.dst.dpId && secondSwP.src.dpId != firstSwP.src.dpId && - !switchIdsConnectedToS42.contains(secondSwP.src.dpId) - } - firstOk && fl2SwPair - } - assumeTrue(fl1SwPair.asBoolean() && fl2SwPair.asBoolean(), - "Required switch pairs were not found in the given topology") + def fl1SwPair = switchPairs.all().withOnlySourceSwitchConnectedToServer42().random() + def fl2SwPair = switchPairs.all() + .excludeSwitches(topology.getActiveServer42Switches()) + .includeSourceSwitch(fl1SwPair.getDst()) + .excludeDestinationSwitches([fl1SwPair.getSrc()]) + .random() + .getReversed() and: "server42 is enabled on the src sw of the first switch pair" def flowRttFeatureStartState = changeFlowRttToggle(true) @@ -526,11 +497,7 @@ class Server42FlowRttSpec extends HealthCheckSpecification { def "Rtt statistic is available for a flow in case switch is not connected to server42"() { given: "Two active switches, only src has server42 connected" - def server42SwIds = topology.getActiveServer42Switches()*.dpId - def switchPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { - it.src.dpId in server42SwIds && !(it.dst.dpId in server42SwIds) - } - assumeTrue(switchPair != null, "Was not able to find a switch with needed connection") + def switchPair = switchPairs.all().withOnlySourceSwitchConnectedToServer42().random() when: "Set server42FlowRtt toggle to true" def flowRttFeatureStartState = changeFlowRttToggle(true) @@ -630,13 +597,16 @@ class Server42FlowRttSpec extends HealthCheckSpecification { data << [ [ flowDescription: "vxlan", - switchPair : { List switchIds -> - getSwPairConnectedToS42ForVxlanFlowOnNonWbSwitch(switchIds) }, - flowTap : { FlowRequestV2 fl -> fl.encapsulationType = FlowEncapsulationType.VXLAN } + switchPair : switchPairs.all() + .withBothSwitchesConnectedToServer42() + .withBothSwitchesVxLanEnabled() + .withSourceSwitchNotManufacturedBy(WB5164) + .random(), + flowTap : { FlowRequestV2 fl -> fl.encapsulationType = VXLAN } ], [ flowDescription: "qinq", - switchPair : { List switchIds -> getSwPairConnectedToS42ForQinQ(switchIds) }, + switchPair : switchPairs.all().withBothSwitchesConnectedToServer42().random(), flowTap : { FlowRequestV2 fl -> fl.source.vlanId = 10 fl.source.innerVlanId = 100 @@ -651,13 +621,7 @@ class Server42FlowRttSpec extends HealthCheckSpecification { @Tags(HARDWARE) //not supported on a local env (the 'stub' service doesn't send real traffic through a switch) def "Flow rtt stats are available after updating switch properties related to server42"(){ given: "Two active switches, src has server42 connected with incorrect config in swProps" - def server42switches = topology.getActiveServer42Switches() - assumeTrue((server42switches.size() > 0), "Unable to find active server42") - def server42switchesDpIds = server42switches*.dpId; - def switchPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { - it.src.dpId in server42switchesDpIds && !server42switchesDpIds.contains(it.dst.dpId) - } - assumeTrue(switchPair != null, "Was not able to find a switch with a server42 connected") + def switchPair = switchPairs.all().withOnlySourceSwitchConnectedToServer42().random() def flowRttFeatureStartState = changeFlowRttToggle(true) def initialFlowRttSw = changeFlowRttSwitch(switchPair.src, true) @@ -741,11 +705,7 @@ class Server42FlowRttSpec extends HealthCheckSpecification { @Tags(HARDWARE) def "Rtt statistic is available for a flow on a LAG port"() { given: "Two active switches, both have server42 connected" - def server42SwIds = topology.getActiveServer42Switches()*.dpId - def switchPair = topologyHelper.switchPairs.collectMany { [it, it.reversed] }.find { - [it.src, it.dst].every { it.dpId in server42SwIds} - } - assumeTrue(switchPair != null, "Was not able to find switches with needed connection") + def switchPair = switchPairs.all().withBothSwitchesConnectedToServer42().random() and: "server42FlowRtt toggle is set to true" def flowRttFeatureStartState = changeFlowRttToggle(true) @@ -837,44 +797,4 @@ class Server42FlowRttSpec extends HealthCheckSpecification { } } } - - def "getSwPairConnectedToS42ForSimpleFlow"(List switchIdsConnectedToS42) { - getTopologyHelper().getSwitchPairs().collectMany { [it, it.reversed] }.find { swP -> - [swP.dst, swP.src].every { it.dpId in switchIdsConnectedToS42 } - } - } - - def "getSwPairConnectedToS42ForProtectedFlow"(List switchIdsConnectedToS42) { - getTopologyHelper().getSwitchPairs().find { - [it.dst, it.src].every { it.dpId in switchIdsConnectedToS42 } && it.paths.unique(false) { - a, b -> a.intersect(b) == [] ? 1 : 0 - }.size() >= 2 - } - } - - def "getSwPairConnectedToS42ForVxlanFlowOnNonWbSwitch"(List switchIdsConnectedToS42) { - getTopologyHelper().getSwitchPairs().find { swP -> - [swP.dst, swP.src].every { it.dpId in switchIdsConnectedToS42 && !it.wb5164 } && swP.paths.findAll { path -> - pathHelper.getInvolvedSwitches(path).every { switchHelper.isVxlanEnabled(it.dpId) } - } - } - } - - def "getSwPairConnectedToS42ForQinQ"(List switchIdsConnectedToS42) { - getTopologyHelper().getSwitchPairs().find { swP -> - [swP.dst, swP.src].every { it.dpId in switchIdsConnectedToS42 && !it.wb5164 } - } - } - - def "getSwPairConnectedToS42ForVxlanFlowOnWbSwitch"(List switchIdsConnectedToS42) { - getTopologyHelper().getSwitchPairs(true).find { swP -> - swP.src.wb5164 && [swP.dst, swP.src].every { it.dpId in switchIdsConnectedToS42 && !it.wb5164 } && - swP.paths.findAll { path -> - pathHelper.getInvolvedSwitches(path).every { - getNorthbound().getSwitchProperties(it.dpId).supportedTransitEncapsulation - .contains(FlowEncapsulationType.VXLAN.toString().toLowerCase()) - } - } - } - } } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/server42/Server42IslRttSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/server42/Server42IslRttSpec.groovy index 71812d10ac7..57aa835d36d 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/server42/Server42IslRttSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/server42/Server42IslRttSpec.groovy @@ -314,6 +314,7 @@ class Server42IslRttSpec extends HealthCheckSpecification { } } + @Tags([HARDWARE]) def "SERVER_42_ISL_RTT rules are updated according to changes in swProps"() { def server42switchIds = topology.getActiveServer42Switches()*.dpId def sw = topology.getActiveServer42Switches().find { it.wb5164 && it.dpId in server42switchIds } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/stats/FlowStatSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/stats/FlowStatSpec.groovy index 14f177b3155..4e36d802ca8 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/stats/FlowStatSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/stats/FlowStatSpec.groovy @@ -51,11 +51,7 @@ class FlowStatSpec extends HealthCheckSpecification { def "System is able to collect stats after intentional swapping flow path to protected"() { given: "Two active neighboring switches with two diverse paths at least" - def traffGenSwitches = topology.activeTraffGens*.switchConnected*.dpId - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.src.dpId in traffGenSwitches && it.dst.dpId in traffGenSwitches && - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() def srcSwitchId = switchPair.getSrc().getDpId() and: "Flow with protected path" @@ -128,11 +124,11 @@ class FlowStatSpec extends HealthCheckSpecification { def "System collects stats when a protected flow was intentionally rerouted"() { given: "Two active not neighboring switches with three diverse paths at least" - def traffGenSwitches = topology.activeTraffGens*.switchConnected*.dpId - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.src.dpId in traffGenSwitches && it.dst.dpId in traffGenSwitches && - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 3 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all() + .nonNeighbouring() + .withTraffgensOnBothEnds() + .withAtLeastNNonOverlappingPaths(3) + .random() def srcSwitchId = switchPair.getSrc().getDpId() and: "A flow with protected path" @@ -212,11 +208,11 @@ class FlowStatSpec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "System collects stats when a protected flow was automatically rerouted"() { given: "Two active not neighboring switches with three not overlapping paths at least" - def traffGenSwitches = topology.activeTraffGens*.switchConnected*.dpId - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.src.dpId in traffGenSwitches && it.dst.dpId in traffGenSwitches && - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 3 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all() + .nonNeighbouring() + .withTraffgensOnBothEnds() + .withAtLeastNNonOverlappingPaths(3) + .random() def srcSwitchId = switchPair.getSrc().getDpId() and: "A flow with protected path" @@ -290,10 +286,11 @@ class FlowStatSpec extends HealthCheckSpecification { def "System collects stat when protected flow is DEGRADED"() { given: "Two active not neighboring switches with two not overlapping paths at least" def traffGenSwitches = topology.activeTraffGens*.switchConnected*.dpId - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.src.dpId in traffGenSwitches && it.dst.dpId in traffGenSwitches && - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all() + .nonNeighbouring() + .withTraffgensOnBothEnds() + .withAtLeastNNonOverlappingPaths(2) + .random() def srcSwitchId = switchPair.getSrc().getDpId() and: "A flow with protected path" @@ -366,10 +363,7 @@ class FlowStatSpec extends HealthCheckSpecification { @Tags([SMOKE_SWITCHES]) def "System collects stats when flow is pinned and unmetered"() { given: "Two active not neighboring switches" - def traffGenSwitches = topology.activeTraffGens*.switchConnected*.dpId - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.src.dpId in traffGenSwitches && it.dst.dpId in traffGenSwitches - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withTraffgensOnBothEnds().random() def srcSwitchId = switchPair.getSrc().getDpId() and: "An unmetered flow" @@ -405,10 +399,7 @@ class FlowStatSpec extends HealthCheckSpecification { def "System is able to collect stats after partial updating(port) on a flow endpoint"() { given: "Two active neighboring switches connected to the traffgens" - def traffGenSwitches = topology.activeTraffGens*.switchConnected*.dpId - def switchPair = topologyHelper.switchPairs.find { - [it.src, it.dst].every { it.dpId in traffGenSwitches } - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random() def srcSwitchId = switchPair.getSrc().getDpId() and: "A flow with updated port on src endpoint via partial update" @@ -447,10 +438,7 @@ class FlowStatSpec extends HealthCheckSpecification { def "System is able to collect stats after partial updating(vlan) on a flow endpoint"() { given: "Two active neighboring switches connected to the traffgens" - def traffGenSwitches = topology.activeTraffGens*.switchConnected*.dpId - def switchPair = topologyHelper.switchPairs.find { - [it.src, it.dst].every { it.dpId in traffGenSwitches } - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random() def srcSwitchId = switchPair.getSrc().getDpId() and: "A flow with updated vlan on src endpoint via partial update" @@ -488,10 +476,7 @@ class FlowStatSpec extends HealthCheckSpecification { def "System is able to collect stats after partial updating(inner vlan) on a flow endpoint"() { given: "Two active neighboring switches connected to the traffgens" - def traffGenSwitches = topology.activeTraffGens*.switchConnected*.dpId - def switchPair = topologyHelper.switchPairs.find { - [it.src, it.dst].every { it.dpId in traffGenSwitches } - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withTraffgensOnBothEnds().random() and: "A flow with updated inner vlan on src endpoint via partial update" diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/DefaultRulesSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/DefaultRulesSpec.groovy index d550b63aa1c..9abefff1082 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/DefaultRulesSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/DefaultRulesSpec.groovy @@ -3,6 +3,7 @@ package org.openkilda.functionaltests.spec.switches import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs import static org.assertj.core.api.Assertions.assertThat import static org.junit.jupiter.api.Assumptions.assumeTrue +import static org.openkilda.functionaltests.extension.tags.Tag.HARDWARE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE_SWITCHES import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT @@ -371,7 +372,7 @@ class DefaultRulesSpec extends HealthCheckSpecification { ].combinations() } - @Tags([TOPOLOGY_DEPENDENT, SMOKE_SWITCHES]) + @Tags([TOPOLOGY_DEPENDENT, SMOKE_SWITCHES, HARDWARE]) def "Able to delete/install the server42 Flow RTT turning rule on a switch"() { setup: "Select a switch which support server42 turning rule" def sw = topology.activeSwitches.find { it.features.contains(SwitchFeature.NOVIFLOW_SWAP_ETH_SRC_ETH_DST) } ?: diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/FlowRulesSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/FlowRulesSpec.groovy index a62d64c48ff..c700cb1e53a 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/FlowRulesSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/FlowRulesSpec.groovy @@ -393,11 +393,8 @@ class FlowRulesSpec extends HealthCheckSpecification { @Tags([TOPOLOGY_DEPENDENT]) def "Able to validate and sync missing rules for #description on terminating/transit switches"() { - given: "Two active not neighboring switches with the longest available path" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().max { pair -> - pair.paths.max { it.size() }.size() - } - assumeTrue(switchPair as boolean, "Unable to find required switches in topology") + given: "Two active not neighboring switches with a long available path" + def switchPair = switchPairs.all().nonNeighbouring().random() def longPath = switchPair.paths.max { it.size() } switchPair.paths.findAll { it != longPath }.each { pathHelper.makePathMorePreferable(longPath, it) } @@ -484,9 +481,7 @@ class FlowRulesSpec extends HealthCheckSpecification { @Tags([LOW_PRIORITY])//uses legacy 'rules validation', has a switchValidate analog in SwitchValidationSpec def "Able to synchronize rules for a flow with protected path"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() and: "Create a flow with protected path" def flow = flowHelperV2.randomFlow(switchPair) @@ -709,11 +704,7 @@ class FlowRulesSpec extends HealthCheckSpecification { @Tags([TOPOLOGY_DEPENDENT, LOW_PRIORITY, SMOKE_SWITCHES])//uses legacy 'rules validation', has a switchValidate analog in SwitchValidationSpec def "Able to synchronize rules for a flow with VXLAN encapsulation"() { given: "Two active not neighboring Noviflow switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { swP -> - swP.paths.find { path -> - pathHelper.getInvolvedSwitches(path).every { switchHelper.isVxlanEnabled(it.dpId) } - } - } ?: assumeTrue(false, "Unable to find required switches in topology") + def switchPair = switchPairs.all().nonNeighbouring().withBothSwitchesVxLanEnabled().random() and: "Create a flow with vxlan encapsulation" def flow = flowHelperV2.randomFlow(switchPair) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/LagPortSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/LagPortSpec.groovy index 970f894ce0d..926157837fd 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/LagPortSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/LagPortSpec.groovy @@ -10,6 +10,7 @@ import org.openkilda.functionaltests.error.flow.FlowNotCreatedExpectedError import static groovyx.gpars.GParsPool.withPool import static org.junit.jupiter.api.Assumptions.assumeTrue import static org.openkilda.functionaltests.extension.tags.Tag.HARDWARE +import static org.openkilda.functionaltests.model.switches.Manufacturer.WB5164 import static org.openkilda.model.MeterId.LACP_REPLY_METER_ID import static org.openkilda.model.cookie.Cookie.DROP_SLOW_PROTOCOLS_LOOP_COOKIE import static org.openkilda.testing.Constants.NON_EXISTENT_SWITCH_ID @@ -161,11 +162,7 @@ class LagPortSpec extends HealthCheckSpecification { def "Able to create a flow on a LAG port"() { given: "A switchPair with a LAG port on the src switch" - def allTraffGenSwitchIds = topology.activeTraffGens*.switchConnected*.dpId - assumeTrue(allTraffGenSwitchIds.size() > 1, "Unable to find required switches in topology") - def switchPair = topologyHelper.getSwitchPairs().find { - [it.src, it.dst].every { it.dpId in allTraffGenSwitchIds } - } + def switchPair = switchPairs.all().withTraffgensOnBothEnds().random() def traffgenSrcSwPort = switchPair.src.traffGens.switchPort[0] def portsArray = (topology.getAllowedPortsForSwitch(switchPair.src)[-2, -1] << traffgenSrcSwPort).unique() def payload = new LagPortRequest(portNumbers: portsArray) @@ -269,7 +266,7 @@ class LagPortSpec extends HealthCheckSpecification { def "Unable to delete a LAG port in case flow on it"() { given: "A flow on a LAG port" - def switchPair = topologyHelper.getSwitchPairs().first() + def switchPair = switchPairs.all().random() def portsArray = topology.getAllowedPortsForSwitch(switchPair.src)[-2, -1] def payload = new LagPortRequest(portNumbers: portsArray) def lagPort = northboundV2.createLagLogicalPort(switchPair.src.dpId, payload).logicalPortNumber @@ -940,7 +937,7 @@ occupied by other LAG group\(s\)./).matches(exc) def "Unable decrease bandwidth on LAG port lower than connected flows bandwidth sum"() { given: "Flows on a LAG port with switch ports" - def switchPair = topologyHelper.getSwitchPairs().first() + def switchPair = switchPairs.all().random() def testPorts = topology.getAllowedPortsForSwitch(switchPair.src).takeRight(2).sort() assert testPorts.size > 1 def maximumBandwidth = testPorts.sum { northbound.getPort(switchPair.src.dpId, it).currentSpeed } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/MetersSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/MetersSpec.groovy index 00b1ae4ee56..2265d03fb84 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/MetersSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/MetersSpec.groovy @@ -1,11 +1,17 @@ package org.openkilda.functionaltests.spec.switches +import org.openkilda.functionaltests.model.switches.Manufacturer + import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs import static org.junit.jupiter.api.Assumptions.assumeTrue import static org.openkilda.functionaltests.extension.tags.Tag.HARDWARE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE_SWITCHES import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT +import static org.openkilda.functionaltests.model.switches.Manufacturer.CENTEC +import static org.openkilda.functionaltests.model.switches.Manufacturer.NOVIFLOW +import static org.openkilda.functionaltests.model.switches.Manufacturer.OVS +import static org.openkilda.functionaltests.model.switches.Manufacturer.WB5164 import static org.openkilda.model.MeterId.MAX_SYSTEM_RULE_METER_ID import static org.openkilda.model.MeterId.createMeterIdForDefaultRule import static org.openkilda.model.cookie.Cookie.ARP_POST_INGRESS_COOKIE @@ -49,6 +55,7 @@ class MetersSpec extends HealthCheckSpecification { static MIN_RATE_KBPS = 64 static CENTEC_MIN_BURST = 1024 // Driven by the Centec specification static CENTEC_MAX_BURST = 32000 // Driven by the Centec specification + static final String NOT_OVS_REGEX = /^(?!.*\bOVS\b).*/ @Value('${burst.coefficient}') double burstCoefficient @@ -58,6 +65,7 @@ class MetersSpec extends HealthCheckSpecification { } @Tags([TOPOLOGY_DEPENDENT, SMOKE, SMOKE_SWITCHES]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Able to delete a meter from a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -96,6 +104,7 @@ class MetersSpec extends HealthCheckSpecification { } @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Unable to delete a meter with invalid ID=#meterId on a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -192,8 +201,8 @@ class MetersSpec extends HealthCheckSpecification { assumeTrue(false, "Unable to find Noviflow Wb5164 switches in topology")) } - @Tags([TOPOLOGY_DEPENDENT]) - @IterationTag(tags = [SMOKE_SWITCHES], iterationNameRegex = /ignore_bandwidth=false/) + @Tags([TOPOLOGY_DEPENDENT, SMOKE_SWITCHES]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Meters are created/deleted when creating/deleting a single-switch flow with ignore_bandwidth=#ignoreBandwidth \ on a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -257,6 +266,7 @@ on a #switchType switch"() { } @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Meters are not created when creating a single-switch flow with maximum_bandwidth=0 on a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -290,12 +300,14 @@ on a #switchType switch"() { } @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Source/destination switches have meters only in flow ingress rule and intermediate switches don't have \ -meters in flow rules at all (#data.flowType flow)"() { - assumeTrue(data.switchPair != null, "Unable to find required switch pair in topology") +meters in flow rules at all (#srcSwitch - #dstSwitch flow)"() { + def switchPair = switchPairs.all().nonNeighbouring() + .withSwitchesManufacturedBy(srcSwitch, dstSwitch).random() when: "Create a flow between given switches" - def flow = flowHelperV2.randomFlow(data.switchPair) + def flow = flowHelperV2.randomFlow(switchPair) flowHelperV2.addFlow(flow) then: "The source and destination switches have only one meter in the flow's ingress rule" @@ -350,45 +362,16 @@ meters in flow rules at all (#data.flowType flow)"() { flow && flowHelperV2.deleteFlow(flow.flowId) where: - data << [ - [ - flowType : "Centec-Centec", - switchPair: getTopologyHelper().getAllNotNeighboringSwitchPairs().find { - it.src.centec && it.dst.centec && hasOf13Path(it) - } - ], - [ - flowType : "Noviflow-Noviflow", - switchPair: getTopologyHelper().getAllNotNeighboringSwitchPairs().find { - it.src.noviflow && it.src.ofVersion == "OF_13" && - it.dst.noviflow && it.dst.ofVersion == "OF_13" && hasOf13Path(it) - } - ], - //TODO(rtretiak): unlock above iterations by introducing a more clever cost manipulation - [ - flowType : "Centec-Noviflow", - switchPair: getTopologyHelper().getAllNotNeighboringSwitchPairs().find { - ((it.src.centec && it.dst.noviflow && it.dst.ofVersion == "OF_13") || - (it.src.noviflow && it.src.ofVersion == "OF_13" && it.dst.centec)) && - hasOf13Path(it) - } - ], - [ - flowType : "Noviflow_Wb5164-Noviflow_Wb5164", - switchPair: getTopologyHelper().getAllNotNeighboringSwitchPairs().find { - it.src.wb5164 && it.dst.wb5164 && hasOf13Path(it) - } - ], - [ - flowType : "OVS-OVS", - switchPair: getTopologyHelper().getAllNotNeighboringSwitchPairs().find { - it.src.virtual && it.dst.virtual && hasOf13Path(it) - } - ] - ] + srcSwitch | dstSwitch + CENTEC | CENTEC + NOVIFLOW | NOVIFLOW + CENTEC | NOVIFLOW + WB5164 | WB5164 + OVS | OVS } @Tags([TOPOLOGY_DEPENDENT, SMOKE_SWITCHES]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Meter burst size is correctly set on #data.switchType switches for #flowRate flow rate"() { setup: "A single-switch flow with #flowRate kbps bandwidth is created on OpenFlow 1.3 compatible switch" def switches = data.switches @@ -550,6 +533,7 @@ meters in flow rules at all (#data.flowType flow)"() { } @Tags([TOPOLOGY_DEPENDENT, SMOKE_SWITCHES]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "System allows to reset meter values to defaults without reinstalling rules for #data.description flow"() { given: "Switches combination (#data.description)" assumeTrue(data.switches.size() > 1, "Desired switch combination is not available in current topology") @@ -713,15 +697,6 @@ meters in flow rules at all (#data.flowType flow)"() { def flowMeters = { it.meterId > MAX_SYSTEM_RULE_METER_ID } - boolean hasOf13Path(SwitchPair pair) { - def possibleDefaultPaths = pair.paths.findAll { - it.size() == pair.paths.min { it.size() }.size() - } - !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - } - void verifyBurstSizeOnWb5164(Long expected, Long actual) { //...ValidationServiceImpl.E_SWITCH_METER_RATE_EQUALS_DELTA_COEFFICIENT = 0.01 assert Math.abs(expected - actual) <= expected * 0.01 diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchActivationSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchActivationSpec.groovy index 98955ae81a4..c3c6e6522d1 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchActivationSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchActivationSpec.groovy @@ -97,7 +97,7 @@ class SwitchActivationSpec extends HealthCheckSpecification { } - @Tags([HARDWARE]) + @Tags([SMOKE_SWITCHES]) def "Excess transitVlanRules/meters are synced from a new switch before connecting to the controller"() { given: "A switch with excess rules/meters and not connected to the controller" def sw = topology.getActiveSwitches().first() @@ -171,7 +171,7 @@ class SwitchActivationSpec extends HealthCheckSpecification { blockData && !switchValidationInfo && switchHelper.reviveSwitch(sw, blockData, true) } - @Tags([HARDWARE]) + @Tags([SMOKE_SWITCHES]) def "Excess vxlanRules/meters are synced from a new switch before connecting to the controller"() { given: "A switch with excess rules/meters and not connected to the controller" def sw = topology.getActiveSwitches().find { switchHelper.isVxlanEnabled(it.dpId) } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchFailuresSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchFailuresSpec.groovy index 7190185e560..0dfc463777f 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchFailuresSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchFailuresSpec.groovy @@ -92,7 +92,7 @@ class SwitchFailuresSpec extends HealthCheckSpecification { @Ignore("https://github.com/telstra/open-kilda/issues/3398") def "System is able to finish the reroute if switch blinks in the middle of it"() { given: "A flow" - def swPair = topologyHelper.allNotNeighboringSwitchPairs.find { it.paths.size() > 1 } + def swPair = switchPairs.all().nonNeighbouring().withAtLeastNPaths(2).random() def flow = flowHelperV2.addFlow(flowHelperV2.randomFlow(swPair)) when: "Current path breaks and reroute starts" diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchMaintenanceSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchMaintenanceSpec.groovy index 17bec98b48d..d2c3d88a524 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchMaintenanceSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchMaintenanceSpec.groovy @@ -70,7 +70,7 @@ class SwitchMaintenanceSpec extends HealthCheckSpecification { given: "Two active not neighboring switches and a switch to be maintained" TopologyDefinition.Switch sw List path - def switchPair = topologyHelper.switchPairs.find { + def switchPair = switchPairs.all().nonNeighbouring().getSwitchPairs().find { path = it.paths.find { aPath -> sw = pathHelper.getInvolvedSwitches(aPath).find { aSw -> it.paths.findAll { it != aPath }.find { !pathHelper.getInvolvedSwitches(it).contains(aSw) } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchPropertiesSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchPropertiesSpec.groovy index 646b155861c..66f844eae6b 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchPropertiesSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchPropertiesSpec.groovy @@ -188,26 +188,4 @@ class SwitchPropertiesSpec extends HealthCheckSpecification { String server42IslRtt Pattern description = null } - - @Tags([TOPOLOGY_DEPENDENT, SMOKE_SWITCHES]) - def "System forbids to turn on VXLAN encap type on switch that does not support it"() { - given: "Switch that does not support VXLAN feature" - def sw = topology.activeSwitches.find { !it.features.contains(SwitchFeature.NOVIFLOW_PUSH_POP_VXLAN) - && !it.features.contains(KILDA_OVS_PUSH_POP_MATCH_VXLAN) } - assumeTrue(sw as boolean, "There is no non-vxlan switch in the topology") - - when: "Try to turn on VXLAN encap type on that switch" - def initProps = switchHelper.getCachedSwProps(sw.dpId) - northbound.updateSwitchProperties(sw.dpId, initProps.jacksonCopy().tap { - it.supportedTransitEncapsulation = [FlowEncapsulationType.VXLAN.toString()] - }) - - then: "Error is returned" - def e = thrown(HttpClientErrorException) - new SwitchPropertiesNotUpdatedExpectedError("Failed to update switch properties.", - ~/Switch $sw.dpId must support at least one of the next features: \[NOVIFLOW_PUSH_POP_VXLAN, \ -KILDA_OVS_PUSH_POP_MATCH_VXLAN\]/).matches(e) - cleanup: - !e && SwitchHelper.updateSwitchProperties(sw, initProps) - } } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchSyncSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchSyncSpec.groovy index 754d8483d85..2de77328e1b 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchSyncSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchSyncSpec.groovy @@ -81,8 +81,7 @@ class SwitchSyncSpec extends HealthCheckSpecification { def "Able to synchronize switch (install missing rules and meters)"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.allNotNeighboringSwitchPairs.find { it.src.ofVersion != "OF_12" && - it.dst.ofVersion != "OF_12" } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().random() and: "Create an intermediate-switch flow" def flow = flowHelperV2.randomFlow(switchPair) @@ -230,12 +229,8 @@ class SwitchSyncSpec extends HealthCheckSpecification { } def "Able to synchronize switch with 'vxlan' rule(install missing rules and meters)"() { - given: "Two active not neighboring Noviflow switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { swP -> - swP.paths.find { path -> - pathHelper.getInvolvedSwitches(path).every { switchHelper.isVxlanEnabled(it.dpId) } - } - } ?: assumeTrue(false, "Unable to find required switches in topology") + given: "Two active not neighboring switches which support VXLAN encapsulation" + def switchPair = switchPairs.all().nonNeighbouring().withBothSwitchesVxLanEnabled().random() and: "Create a flow with vxlan encapsulation" def flow = flowHelperV2.randomFlow(switchPair) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationSingleSwFlowSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationSingleSwFlowSpec.groovy index 22db052bfa1..9063f786271 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationSingleSwFlowSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationSingleSwFlowSpec.groovy @@ -1,12 +1,15 @@ package org.openkilda.functionaltests.spec.switches +import org.openkilda.functionaltests.extension.tags.IterationTag import org.openkilda.messaging.model.FlowDirectionType import static org.junit.jupiter.api.Assumptions.assumeTrue +import static org.openkilda.functionaltests.extension.tags.Tag.HARDWARE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE_SWITCHES import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT import static org.openkilda.functionaltests.helpers.SwitchHelper.isDefaultMeter +import static org.openkilda.functionaltests.spec.switches.MetersSpec.NOT_OVS_REGEX import static org.openkilda.model.MeterId.MAX_SYSTEM_RULE_METER_ID import static org.openkilda.model.MeterId.MIN_FLOW_METER_ID import static org.openkilda.testing.Constants.RULES_INSTALLATION_TIME @@ -46,7 +49,7 @@ Description of fields: - excess - those meters/rules, which are present on a switch, but are NOT present in db - proper - meters/rules values are the same on a switch and in db """) -@Tags([SMOKE_SWITCHES]) +@Tags([SMOKE_SWITCHES, TOPOLOGY_DEPENDENT]) class SwitchValidationSingleSwFlowSpec extends HealthCheckSpecification { @Value("#{kafkaTopicsConfig.getSpeakerSwitchManagerTopic()}") String speakerTopic @@ -58,7 +61,8 @@ class SwitchValidationSingleSwFlowSpec extends HealthCheckSpecification { deleteAnyFlowsLeftoversIssue5480() } - @Tags([TOPOLOGY_DEPENDENT, SMOKE]) + @Tags([SMOKE]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Switch validation is able to store correct information on a #switchType switch in the 'proper' section"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -120,7 +124,7 @@ class SwitchValidationSingleSwFlowSpec extends HealthCheckSpecification { "OVS" | getVirtualSwitches() } - @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Switch validation is able to detect meter info into the 'misconfigured' section on a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -235,7 +239,7 @@ class SwitchValidationSingleSwFlowSpec extends HealthCheckSpecification { "OVS" | getVirtualSwitches() } - @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Switch validation is able to detect meter info into the 'missing' section on a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -305,7 +309,7 @@ class SwitchValidationSingleSwFlowSpec extends HealthCheckSpecification { "OVS" | getVirtualSwitches() } - @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Switch validation is able to detect meter info into the 'excess' section on a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -380,7 +384,7 @@ class SwitchValidationSingleSwFlowSpec extends HealthCheckSpecification { "OVS" | getVirtualSwitches() } - @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Switch validation is able to detect rule info into the 'missing' section on a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -438,7 +442,7 @@ class SwitchValidationSingleSwFlowSpec extends HealthCheckSpecification { "OVS" | getVirtualSwitches() } - @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Switch validation is able to detect rule/meter info into the 'excess' section on a #switchType switch"() { assumeTrue(switches as boolean, "Unable to find required switches in topology") @@ -543,22 +547,7 @@ class SwitchValidationSingleSwFlowSpec extends HealthCheckSpecification { "OVS" | getVirtualSwitches() } - def "Able to get the switch validate info on a NOT supported switch"() { - given: "Not supported switch" - def sw = topology.activeSwitches.find { it.ofVersion == "OF_12" } - assumeTrue(sw as boolean, "Unable to find required switches in topology") - - when: "Try to invoke the switch validate request" - def response = northbound.validateSwitch(sw.dpId) - - then: "Response without meter section is returned" - response.rules.proper.findAll { !new Cookie(it).serviceFlag }.empty - response.rules.missing.empty - response.rules.excess.empty - !response.meters - } - - @Tags([TOPOLOGY_DEPENDENT]) + @IterationTag(tags = [HARDWARE], iterationNameRegex = NOT_OVS_REGEX) def "Able to validate and sync a #switchType switch having missing rules of single-port single-switch flow"() { assumeTrue(sw as boolean, "Unable to find $switchType switch in topology") given: "A single-port single-switch flow" diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationSpec.groovy index a75d11c0e6e..01eb16aa2d2 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationSpec.groovy @@ -147,13 +147,7 @@ class SwitchValidationSpec extends HealthCheckSpecification { def "Able to validate and sync a transit switch with proper rules and no meters"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { pair -> - def possibleDefaultPaths = pair.paths.findAll { it.size() == pair.paths.min { it.size() }.size() } - //ensure the path won't have only OF_12 intermediate switches - !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - } ?: assumeTrue(false, "No not-neighbouring switch pairs found") + def switchPair = switchPairs.all().nonNeighbouring().random() when: "Create an intermediate-switch flow" def flow = flowHelperV2.addFlow(flowHelperV2.randomFlow(switchPair)) @@ -489,14 +483,7 @@ misconfigured" def "Able to validate and sync a switch with missing transit rule"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { pair -> - def possibleDefaultPaths = pair.paths.findAll { it.size() == pair.paths.min { it.size() }.size() } - //ensure the path won't have only OF_12 intermediate switches - def hasOf13Path = !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - hasOf13Path && pair.src.ofVersion != "OF_12" && pair.dst.ofVersion != "OF_12" - } ?: assumeTrue(false, "No not-neighbouring switch pairs found") + def switchPair = switchPairs.all().nonNeighbouring().random() and: "Create an intermediate-switch flow" def flow = flowHelperV2.randomFlow(switchPair) @@ -560,14 +547,7 @@ misconfigured" def "Able to validate and sync a switch with missing egress rule"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { pair -> - def possibleDefaultPaths = pair.paths.findAll { it.size() == pair.paths.min { it.size() }.size() } - //ensure the path won't have only OF_12 intermediate switches - def hasOf13Path = !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - hasOf13Path && pair.src.ofVersion != "OF_12" && pair.dst.ofVersion != "OF_12" - } ?: assumeTrue(false, "No not-neighbouring switch pairs found") + def switchPair = switchPairs.all().nonNeighbouring().random() and: "Create an intermediate-switch flow" def flow = flowHelperV2.randomFlow(switchPair) @@ -641,14 +621,7 @@ misconfigured" def "Able to validate and sync an excess ingress/egress/transit rule + meter"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { pair -> - def possibleDefaultPaths = pair.paths.findAll { it.size() == pair.paths.min { it.size() }.size() } - //ensure the path won't have only OF_12 intermediate switches - def hasOf13Path = !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - hasOf13Path && pair.src.ofVersion != "OF_12" && pair.dst.ofVersion != "OF_12" - } ?: assumeTrue(false, "Unable to find required switches in topology") + def switchPair = switchPairs.all().nonNeighbouring().random() and: "Create an intermediate-switch flow" def flow = flowHelperV2.addFlow(flowHelperV2.randomFlow(switchPair)) @@ -778,11 +751,7 @@ misconfigured" @Tags(TOPOLOGY_DEPENDENT) def "Able to validate and sync a switch with missing 'vxlan' ingress/transit/egress rule + meter"() { given: "Two active not neighboring VXLAN supported switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { swP -> - swP.paths.find { path -> - pathHelper.getInvolvedSwitches(path).every { switchHelper.isVxlanEnabled(it.dpId) } - } - } ?: assumeTrue(false, "Unable to find required switches in topology") + def switchPair = switchPairs.all().nonNeighbouring().withBothSwitchesVxLanEnabled().random() and: "Create a flow with vxlan encapsulation" def flow = flowHelperV2.addFlow(flowHelperV2.randomFlow(switchPair).tap {it.encapsulationType = FlowEncapsulationType.VXLAN}) @@ -895,9 +864,7 @@ misconfigured" def "Able to validate and sync a missing 'protected path' egress rule"() { given: "A flow with protected path" - def swPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No switch pair with at least 2 diverse paths") + def swPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() def flow = flowHelperV2.randomFlow(swPair).tap { allocateProtectedPath = true } flowHelperV2.addFlow(flow) def flowInfo = northbound.getFlowPath(flow.flowId) @@ -958,9 +925,7 @@ misconfigured" def "Able to validate and sync a missing 'connected device' #data.descr rule"() { given: "A flow with enabled connected devices" - def swPair = topologyHelper.switchPairs.find { - [it.src, it.dst].every { it.features.contains(SwitchFeature.MULTI_TABLE) } - } + def swPair = switchPairs.all().random() Map initialProps = [swPair.src, swPair.dst] .collectEntries { [(it): switchHelper.getCachedSwProps(it.getDpId())] } def flow = flowHelper.randomFlow(swPair) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationV2Spec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationV2Spec.groovy index ab8c3461233..8e3a28e2868 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationV2Spec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchValidationV2Spec.groovy @@ -158,13 +158,7 @@ class SwitchValidationV2Spec extends HealthCheckSpecification { def "Able to validate and sync a transit switch with proper rules and no meters"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { pair -> - def possibleDefaultPaths = pair.paths.findAll { it.size() == pair.paths.min { it.size() }.size() } - //ensure the path won't have only OF_12 intermediate switches - !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - } ?: assumeTrue(false, "No not-neighbouring switch pairs found") + def switchPair = switchPairs.all().nonNeighbouring().random() when: "Create an intermediate-switch flow" def flow = flowHelperV2.randomFlow(switchPair) @@ -495,14 +489,7 @@ misconfigured" def "Able to validate and sync a switch with missing transit rule"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { pair -> - def possibleDefaultPaths = pair.paths.findAll { it.size() == pair.paths.min { it.size() }.size() } - //ensure the path won't have only OF_12 intermediate switches - def hasOf13Path = !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - hasOf13Path && pair.src.ofVersion != "OF_12" && pair.dst.ofVersion != "OF_12" - } ?: assumeTrue(false, "No not-neighbouring switch pairs found") + def switchPair = switchPairs.all().nonNeighbouring().random() and: "Create an intermediate-switch flow" def flow = flowHelperV2.randomFlow(switchPair) flowHelperV2.addFlow(flow) @@ -559,28 +546,25 @@ misconfigured" def "Able to validate and sync a switch with missing egress rule"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { pair -> - def possibleDefaultPaths = pair.paths.findAll { it.size() == pair.paths.min { it.size() }.size() } - //ensure the path won't have only OF_12 intermediate switches - def hasOf13Path = !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - hasOf13Path && pair.src.ofVersion != "OF_12" && pair.dst.ofVersion != "OF_12" - } ?: assumeTrue(false, "No not-neighbouring switch pairs found") + def switchPair = switchPairs.all().nonNeighbouring().random() + and: "Create an intermediate-switch flow" def flow = flowHelperV2.randomFlow(switchPair) flowHelperV2.addFlow(flow) def rulesOnSrc = northbound.getSwitchRules(switchPair.src.dpId).flowEntries def rulesOnDst = northbound.getSwitchRules(switchPair.dst.dpId).flowEntries + when: "Delete created rules on the srcSwitch" def egressCookie = database.getFlow(flow.flowId).reversePath.cookie.value northbound.deleteSwitchRules(switchPair.src.dpId, egressCookie) + then: "Rule info is moved into the 'missing' section on the srcSwitch" verifyAll(northboundV2.validateSwitch(switchPair.src.dpId)) { it.rules.missing*.cookie == [egressCookie] it.rules.proper.size() == rulesOnSrc.size() - 1 it.rules.excess.empty } + and: "Rule info is NOT moved into the 'missing' section on the dstSwitch and transit switches" def dstSwitchValidateInfo = northboundV2.validateSwitch(switchPair.dst.dpId) dstSwitchValidateInfo.rules.proper*.cookie.sort() == rulesOnDst*.cookie.sort() @@ -592,17 +576,21 @@ misconfigured" assert transitSwitchValidateInfo.rules.proper*.cookie.findAll { !new Cookie(it).serviceFlag }.size() == 2 transitSwitchValidateInfo.verifyRuleSectionsAreEmpty(["missing", "excess"]) } + when: "Synchronize the switch" with(northbound.synchronizeSwitch(switchPair.src.dpId, false)) { rules.installed == [egressCookie] } + then: "Repeated validation shows no discrepancies" verifyAll(northboundV2.validateSwitch(switchPair.dst.dpId)) { it.rules.proper*.cookie.sort() == rulesOnDst*.cookie.sort() it.verifyRuleSectionsAreEmpty(["missing", "excess"]) } + when: "Delete the flow" def deleteFlow = flowHelperV2.deleteFlow(flow.flowId) + then: "Check that the switch validate request returns empty sections on all involved switches" Wrappers.wait(WAIT_OFFSET) { involvedSwitchIds.findAll { !it.description.contains("OF_12") }.each { switchId -> @@ -612,6 +600,7 @@ misconfigured" } } def testIsCompleted = true + cleanup: flow && !deleteFlow && flowHelperV2.deleteFlow(flow.flowId) if (involvedSwitchIds && !testIsCompleted) { @@ -630,14 +619,8 @@ misconfigured" def "Able to validate and sync an excess ingress/egress/transit rule + meter"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { pair -> - def possibleDefaultPaths = pair.paths.findAll { it.size() == pair.paths.min { it.size() }.size() } - //ensure the path won't have only OF_12 intermediate switches - def hasOf13Path = !possibleDefaultPaths.find { path -> - path[1..-2].every { it.switchId.description.contains("OF_12") } - } - hasOf13Path && pair.src.ofVersion != "OF_12" && pair.dst.ofVersion != "OF_12" - } ?: assumeTrue(false, "Unable to find required switches in topology") + def switchPair = switchPairs.all().nonNeighbouring().random() + and: "Create an intermediate-switch flow" def flow = flowHelperV2.addFlow(flowHelperV2.randomFlow(switchPair)) def createdCookiesSrcSw = northbound.getSwitchRules(switchPair.src.dpId).flowEntries*.cookie @@ -756,11 +739,8 @@ misconfigured" @Tags(TOPOLOGY_DEPENDENT) def "Able to validate and sync a switch with missing 'vxlan' ingress/transit/egress rule + meter"() { given: "Two active not neighboring VXLAN supported switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { swP -> - swP.paths.find { path -> - pathHelper.getInvolvedSwitches(path).every { switchHelper.isVxlanEnabled(it.dpId) } - } - } ?: assumeTrue(false, "Unable to find required switches in topology") + def switchPair = switchPairs.all().nonNeighbouring().withBothSwitchesVxLanEnabled().random() + and: "Create a flow with vxlan encapsulation" def flow = flowHelperV2.addFlow(flowHelperV2.randomFlow(switchPair).tap { it.encapsulationType = FlowEncapsulationType.VXLAN}) and: "Remove required rules and meters from switches" @@ -863,9 +843,7 @@ misconfigured" def "Able to validate and sync a missing 'protected path' egress rule"() { given: "A flow with protected path" - def swPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 - } ?: assumeTrue(false, "No switch pair with at least 2 diverse paths") + def swPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() def flow = flowHelperV2.randomFlow(swPair).tap { allocateProtectedPath = true } flowHelperV2.addFlow(flow) def flowInfo = northbound.getFlowPath(flow.flowId) @@ -919,9 +897,7 @@ misconfigured" def "Able to validate and sync a missing 'connected device' #data.descr rule"() { given: "A flow with enabled connected devices" - def swPair = topologyHelper.switchPairs.find { - [it.src, it.dst].every { it.features.contains(SwitchFeature.MULTI_TABLE) } - } + def swPair = switchPairs.all().random() Map initialProps = [swPair.src, swPair.dst] .collectEntries { [(it): switchHelper.getCachedSwProps(it.dpId)] } def flow = flowHelper.randomFlow(swPair) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchesFlowsV2Spec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchesFlowsV2Spec.groovy index f04754f9edb..aeb73f86887 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchesFlowsV2Spec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchesFlowsV2Spec.groovy @@ -55,9 +55,9 @@ class SwitchesFlowsV2Spec extends HealthCheckSpecification { && it.pathsEp1.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 } assumeTrue(switchTriplet != null, "Couldn't find appropriate switch triplet") - switchPair = topologyHelper.getSwitchPairs(false).find { - [it.getSrc(), it.getDst()].toSet() == ([switchTriplet.getShared(), switchTriplet.getEp1()].toSet()) - } + switchPair = switchPairs.all() + .includeSwitch(switchTriplet.getShared()) + .includeSwitch(switchTriplet.getEp1()).random() def flowDefinition = flowHelperV2.randomFlow(switchPair, false).tap { allocateProtectedPath = true } flowId = flowHelperV2.addFlow(flowDefinition).getFlowId() switchFlowGoesThrough = pathHelper.getInvolvedSwitches(flowId).find { diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchesSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchesSpec.groovy index 71e94b639bd..5b2b34867fc 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchesSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/SwitchesSpec.groovy @@ -12,6 +12,8 @@ import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE_SWITCHES import static org.openkilda.functionaltests.helpers.FlowHistoryConstants.REROUTE_ACTION import static org.openkilda.functionaltests.helpers.FlowHistoryConstants.REROUTE_FAIL +import static org.openkilda.functionaltests.model.switches.Manufacturer.CENTEC +import static org.openkilda.functionaltests.model.switches.Manufacturer.CENTEC import static org.openkilda.testing.Constants.NON_EXISTENT_SWITCH_ID import static org.openkilda.testing.Constants.WAIT_OFFSET import static org.openkilda.testing.service.floodlight.model.FloodlightConnectMode.RW @@ -68,9 +70,7 @@ class SwitchesSpec extends HealthCheckSpecification { @Tags(ISL_RECOVER_ON_FAIL) def "Systems allows to get a flow that goes through a switch"() { given: "Two active not neighboring switches with two diverse paths at least" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().find { - it.paths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 3 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().withAtLeastNNonOverlappingPaths(2).random() and: "A protected flow" def protectedFlow = flowHelperV2.randomFlow(switchPair) @@ -195,8 +195,7 @@ class SwitchesSpec extends HealthCheckSpecification { def "Systems allows to get all flows that goes through a DEACTIVATED switch"() { given: "Two active not neighboring switches" - def switchPair = topologyHelper.getAllNotNeighboringSwitchPairs().first() ?: - assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().nonNeighbouring().random() and: "A simple flow" def simpleFlow = flowHelperV2.randomFlow(switchPair) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/toggles/FeatureTogglesV2Spec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/toggles/FeatureTogglesV2Spec.groovy index b1aa9c898da..b0d4d3d27c6 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/toggles/FeatureTogglesV2Spec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/toggles/FeatureTogglesV2Spec.groovy @@ -15,6 +15,7 @@ import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.functionaltests.helpers.FlowHistoryConstants.REROUTE_ACTION import static org.openkilda.functionaltests.helpers.FlowHistoryConstants.REROUTE_FAIL import static org.openkilda.functionaltests.helpers.Wrappers.wait +import static org.openkilda.testing.Constants.RULES_INSTALLATION_TIME import static org.openkilda.testing.Constants.WAIT_OFFSET import org.openkilda.functionaltests.HealthCheckSpecification @@ -134,10 +135,7 @@ class FeatureTogglesV2Spec extends HealthCheckSpecification { def "Flow encapsulation type is changed while auto rerouting according to 'flows_reroute_using_default_encap_type' \ feature toggle"() { given: "A switch pair which supports 'transit_vlan' and 'vxlan' encapsulation types" - def swPair = topologyHelper.getAllNeighboringSwitchPairs().find { - [it.src, it.dst].every { switchHelper.isVxlanEnabled(it.dpId) } && it.paths.size() >= 2 - } - assumeTrue(swPair as boolean, "Unable to find required switches in topology") + def swPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().withAtLeastNPaths(2).random() and: "The 'flows_reroute_using_default_encap_type' feature is enabled" def initFeatureToggle = northbound.getFeatureToggles() @@ -216,17 +214,13 @@ feature toggle"() { database.resetCosts(topology.isls) } - @Ignore("https://github.com/telstra/open-kilda/issues/2955") - @Tags([HARDWARE, ISL_RECOVER_ON_FAIL]) + @Tags([ISL_RECOVER_ON_FAIL, LOW_PRIORITY]) @ResourceLock(DEFAULT_FLOW_ENCAP) def "Flow encapsulation type is not changed while syncing/auto rerouting/updating according to \ 'flows_reroute_using_default_encap_type' if switch doesn't support new type of encapsulation"() { given: "A switch pair which supports 'transit_vlan' and 'vxlan' encapsulation types" - and: "The 'vxlan' encapsulation type is disable in swProps on the src switch" - def swPair = topologyHelper.getAllNeighboringSwitchPairs().find { - [it.src, it.dst].every { switchHelper.isVxlanEnabled(it.dpId) } && it.paths.size() >= 2 - } - assumeTrue(swPair as boolean, "Unable to find required switches in topology") + and: "The 'vxlan' encapsulation type is disabled in swProps on the src switch" + def swPair = switchPairs.all().neighbouring().withBothSwitchesVxLanEnabled().withAtLeastNPaths(2).random() def initSrcSwProps = switchHelper.getCachedSwProps(swPair.src.dpId) northbound.updateSwitchProperties(swPair.src.dpId, initSrcSwProps.jacksonCopy().tap { it.supportedTransitEncapsulation = [FlowEncapsulationType.TRANSIT_VLAN.toString()] @@ -257,16 +251,17 @@ feature toggle"() { assert islUtils.getIslInfo(islToBreak).get().state == IslChangeType.FAILED } - then: "Flow is rerouted" - wait(WAIT_OFFSET + rerouteDelay) { - assert pathHelper.convert(northbound.getFlowPath(flow.flowId)) != currentPath - } + then: "Flow is not rerouted" + sleep(rerouteDelay * 1000) + pathHelper.convert(northbound.getFlowPath(flow.flowId)) == currentPath and: "Encapsulation type is NOT changed according to kilda configuration" northboundV2.getFlow(flow.flowId).encapsulationType != vxlanEncapsulationType.toString().toLowerCase() - and: "Flow is in UP state" - northboundV2.getFlowStatus(flow.flowId).status == FlowState.UP + and: "Flow is in DOWN state" + wait(WAIT_OFFSET) { + northboundV2.getFlowStatus(flow.flowId).status == FlowState.DOWN + } when: "Update the flow" northboundV2.updateFlow(flow.flowId, flow.tap { it.description = description + " updated" }) @@ -277,8 +272,10 @@ feature toggle"() { then: "Encapsulation type is NOT changed according to kilda configuration" northboundV2.getFlow(flow.flowId).encapsulationType != vxlanEncapsulationType.toString().toLowerCase() - and: "Flow is in UP state" - northboundV2.getFlowStatus(flow.flowId).status == FlowState.UP + and: "Flow is rerouted and in UP state" + wait(RULES_INSTALLATION_TIME) { + northboundV2.getFlowStatus(flow.flowId).status == FlowState.UP + } when: "Synchronize the flow" with(northbound.synchronizeFlow(flow.flowId)) { !it.rerouted } @@ -286,7 +283,7 @@ feature toggle"() { then: "Encapsulation type is NOT changed according to kilda configuration" northboundV2.getFlow(flow.flowId).encapsulationType != vxlanEncapsulationType.toString().toLowerCase() - and: "Flow is in UP state" + and: "Flow is still in UP state" northboundV2.getFlowStatus(flow.flowId).status == FlowState.UP cleanup: @@ -308,9 +305,7 @@ feature toggle"() { @Tags([LOW_PRIORITY, ISL_RECOVER_ON_FAIL]) def "System doesn't reroute flow when flows_reroute_on_isl_discovery: false"() { given: "A flow with alternative paths" - def switchPair = topologyHelper.getAllNeighboringSwitchPairs().find { - it.paths.size() >= 2 - } ?: assumeTrue(false, "No suiting switches found") + def switchPair = switchPairs.all().neighbouring().withAtLeastNPaths(2).random() def allFlowPaths = switchPair.paths def flow = flowHelperV2.randomFlow(switchPair) flowHelperV2.addFlow(flow) diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/xresilience/FloodlightKafkaConnectionSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/xresilience/FloodlightKafkaConnectionSpec.groovy index ea8a0bcaa0a..e3db19ff88c 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/xresilience/FloodlightKafkaConnectionSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/xresilience/FloodlightKafkaConnectionSpec.groovy @@ -100,7 +100,7 @@ class FloodlightKafkaConnectionSpec extends HealthCheckSpecification { } and: "System is able to successfully create a valid flow between regions" - def swPair = topologyHelper.switchPairs.find { pair -> + def swPair = switchPairs.all().getSwitchPairs().find { pair -> [pair.src, pair.dst].any { updatedRegions[it.dpId].contains(regionToBreak) } && updatedRegions[pair.src.dpId] != updatedRegions[pair.dst.dpId] } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/xresilience/RetriesSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/xresilience/RetriesSpec.groovy index 4213565b803..10eb6465fe6 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/xresilience/RetriesSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/xresilience/RetriesSpec.groovy @@ -40,7 +40,7 @@ and at least 1 path must remain safe" List mainPath, failoverPath, safePath Switch switchToBreak //will belong to failoverPath and be absent in safePath Isl islToBreak //will be used to break the mainPath. This ISL is not used in safePath or failoverPath - def switchPair = topologyHelper.switchPairs.find { swPair -> + def switchPair = switchPairs.all().getSwitchPairs().find { swPair -> if(swPair.paths.size() >= 3) { failoverPath = swPair.paths.find { failoverPathCandidate -> def failoverSwitches = pathHelper.getInvolvedSwitches(failoverPathCandidate) @@ -130,13 +130,8 @@ and at least 1 path must remain safe" @Tags([SMOKE_SWITCHES, LOCKKEEPER]) def "System tries to retry rule installation during #data.description if previous one is failed"(){ given: "Two active neighboring switches with two diverse paths at least" - def allPaths - def swPair = topologyHelper.getAllNeighboringSwitchPairs().find { - allPaths = it.paths - allPaths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 && - allPaths.find { it.size() > 2 } - } ?: assumeTrue(false, "No switch pair with at least 2 diverse paths") - + def swPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() + def allPaths = swPair.getPaths() List mainPath = allPaths.min { it.size() } //find path with more than two switches List protectedPath = allPaths.findAll { it != mainPath && it.size() != 2 }.min { it.size() } @@ -268,7 +263,7 @@ and at least 1 path must remain safe" def "Flow is successfully deleted from the system even if some rule delete commands fail (no rollback for delete)"() { given: "A flow" - def swPair = topologyHelper.switchPairs.first() + def swPair = switchPairs.all().first() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) @@ -295,22 +290,17 @@ and at least 1 path must remain safe" def "System retries the intentional rerouteV1 if it fails to install rules on a switch"() { given: "Two active neighboring switches with two diverse paths at least(main and backup paths)" - def allPaths - def swPair = topologyHelper.getAllNeighboringSwitchPairs().find { - allPaths = it.paths - allPaths.unique(false) { a, b -> a.intersect(b) == [] ? 1 : 0 }.size() >= 2 && - allPaths.find { it.size() > 2 } - } + def swPair = switchPairs.all().neighbouring().withAtLeastNNonOverlappingPaths(2).random() - List mainPath = allPaths.min { it.size() } + List mainPath = swPair.paths.min { it.size() } //find path with more than two switches - List backupPath = allPaths.findAll { it != mainPath && it.size() != 2 }.min { it.size() } + List backupPath = swPair.paths.findAll { it != mainPath && it.size() != 2 }.min { it.size() } and: "All alternative paths unavailable (bring ports down)" def broughtDownIsls = [] def otherIsls = [] def involvedIsls = (pathHelper.getInvolvedIsls(mainPath) + pathHelper.getInvolvedIsls(backupPath)).unique() - allPaths.findAll { it != mainPath && it != backupPath }.each { + swPair.paths.findAll { it != mainPath && it != backupPath }.each { pathHelper.getInvolvedIsls(it).findAll { !(it in involvedIsls || it.reversed in involvedIsls) }.each { otherIsls.add(it) } @@ -406,7 +396,7 @@ class RetriesIsolatedSpec extends HealthCheckSpecification { //isolation: requires no 'up' events in the system while flow is Down def "System does not retry after global timeout for reroute operation"() { given: "A flow with ability to reroute" - def swPair = topologyHelper.switchPairs.find { it.paths.size() > 1 } + def swPair = switchPairs.all().withAtLeastNPaths(2).random() def flow = flowHelperV2.randomFlow(swPair) flowHelperV2.addFlow(flow) diff --git a/src-java/testing/test-library/src/main/java/org/openkilda/testing/Constants.java b/src-java/testing/test-library/src/main/java/org/openkilda/testing/Constants.java index bf8088ab521..711402bcc81 100644 --- a/src-java/testing/test-library/src/main/java/org/openkilda/testing/Constants.java +++ b/src-java/testing/test-library/src/main/java/org/openkilda/testing/Constants.java @@ -23,6 +23,7 @@ public final class Constants { public static final Integer DEFAULT_COST = 700; public static final Integer WAIT_OFFSET = 15; + public static final Integer HEALTH_CHECK_TIME = 30; public static final Integer PROTECTED_PATH_INSTALLATION_TIME = 20; public static final Integer PATH_INSTALLATION_TIME = 15; public static final Integer FLOW_CRUD_TIMEOUT = 30; diff --git a/src-java/testing/test-library/src/main/java/org/openkilda/testing/config/DefaultServiceConfig.java b/src-java/testing/test-library/src/main/java/org/openkilda/testing/config/DefaultServiceConfig.java index 41255168372..30516f9ac81 100644 --- a/src-java/testing/test-library/src/main/java/org/openkilda/testing/config/DefaultServiceConfig.java +++ b/src-java/testing/test-library/src/main/java/org/openkilda/testing/config/DefaultServiceConfig.java @@ -18,6 +18,7 @@ import static org.springframework.beans.factory.config.ConfigurableBeanFactory.SCOPE_PROTOTYPE; import org.openkilda.testing.model.topology.TopologyDefinition; +import org.openkilda.testing.service.floodlight.FloodlightServiceImpl; import org.openkilda.testing.service.floodlight.IslandFloodlightService; import org.openkilda.testing.service.floodlight.model.Floodlight; import org.openkilda.testing.service.floodlight.model.FloodlightConnectMode; @@ -116,8 +117,7 @@ public List floodlights(@Value("#{'${floodlight.openflows}'.split(', @Value("#{'${floodlight.regions}'.split(',')}") List regions, @Value("#{'${floodlight.modes}'.split(',')}") List modes, @Autowired TopologyDefinition topo) { - if (openflows.size() != endpoints.size() || openflows.size() != containers.size() - || openflows.size() != regions.size() || openflows.size() != modes.size()) { + if (isFloodlightPropsCorrect(openflows, endpoints, containers, regions, modes)) { throw new IllegalArgumentException("Floodlight test properties are illegal. Sizes of floodlight.openflows," + " floodlight.endpoints, floodlight.containers, floodlight.regions, floodlight.modes should be " + "equal"); @@ -131,6 +131,34 @@ public List floodlights(@Value("#{'${floodlight.openflows}'.split(', return floodlights; } + @Bean + @Scope(SCOPE_PROTOTYPE) + public List commonFloodlights(@Value("#{'${floodlight.openflows}'.split(',')}") List openflows, + @Value("#{'${floodlight.endpoints}'.split(',')}") List endpoints, + @Value("#{'${floodlight.containers}'.split(',')}") List containers, + @Value("#{'${floodlight.regions}'.split(',')}") List regions, + @Value("#{'${floodlight.modes}'.split(',')}") List modes, + @Autowired TopologyDefinition topo) { + if (isFloodlightPropsCorrect(openflows, endpoints, containers, regions, modes)) { + throw new IllegalArgumentException("Floodlight test properties are illegal. Sizes of floodlight.openflows," + + " floodlight.endpoints, floodlight.containers, floodlight.regions, floodlight.modes should be " + + "equal"); + } + List floodlights = new ArrayList<>(); + for (int i = 0; i < regions.size(); i++) { + floodlights.add(new Floodlight(openflows.get(i), containers.get(i), regions.get(i), + new FloodlightServiceImpl(endpoints.get(i)), + EnumUtils.getEnumIgnoreCase(FloodlightConnectMode.class, modes.get(i)))); + } + return floodlights; + } + + private boolean isFloodlightPropsCorrect(List openflows, List endpoints, List containers, + List regions, List modes) { + return openflows.size() != endpoints.size() || openflows.size() != containers.size() + || openflows.size() != regions.size() || openflows.size() != modes.size(); + } + @Bean(name = "tsdbRestTemplate") public RestTemplate tsdbRestTemplate(@Value("${tsdb.endpoint}") String endpoint) { return buildLoggingRestTemplate(endpoint);