16 package org.openkilda.atdd.staging.steps;
18 import static com.nitorcreations.Matchers.reflectEquals;
19 import static java.lang.String.format;
20 import static java.util.stream.Collectors.toList;
21 import static org.hamcrest.MatcherAssert.assertThat;
22 import static org.hamcrest.Matchers.contains;
23 import static org.hamcrest.Matchers.containsInAnyOrder;
24 import static org.hamcrest.Matchers.empty;
25 import static org.hamcrest.Matchers.equalTo;
26 import static org.hamcrest.Matchers.everyItem;
27 import static org.hamcrest.Matchers.hasKey;
28 import static org.hamcrest.Matchers.hasProperty;
29 import static org.hamcrest.Matchers.is;
30 import static org.hamcrest.Matchers.isIn;
31 import static org.hamcrest.Matchers.not;
32 import static org.hamcrest.Matchers.notNullValue;
33 import static org.junit.Assert.assertEquals;
34 import static org.junit.Assert.assertFalse;
35 import static org.junit.Assert.assertNotNull;
36 import static org.junit.Assert.assertNull;
37 import static org.junit.Assert.assertTrue;
38 import static org.junit.Assert.fail;
69 import com.google.common.collect.ContiguousSet;
70 import com.google.common.collect.DiscreteDomain;
71 import com.google.common.collect.Range;
72 import com.google.common.collect.RangeSet;
73 import com.google.common.collect.TreeRangeSet;
74 import cucumber.api.java.en.And;
75 import cucumber.api.java.en.Given;
76 import cucumber.api.java.en.Then;
77 import cucumber.api.java.en.When;
78 import cucumber.api.java8.En;
79 import lombok.extern.slf4j.Slf4j;
80 import net.jodah.failsafe.Failsafe;
81 import net.jodah.failsafe.RetryPolicy;
82 import org.apache.commons.collections4.ListValuedMap;
83 import org.apache.commons.collections4.multimap.ArrayListValuedHashMap;
84 import org.springframework.beans.factory.annotation.Autowired;
85 import org.springframework.beans.factory.annotation.Qualifier;
87 import java.text.SimpleDateFormat;
88 import java.util.ArrayList;
89 import java.util.Collections;
90 import java.util.Date;
91 import java.util.HashSet;
92 import java.util.List;
94 import java.util.Map.Entry;
95 import java.util.Objects;
97 import java.util.UUID;
98 import java.util.concurrent.TimeUnit;
99 import java.util.stream.Stream;
123 @Qualifier(
"topologyUnderTest")
126 private Set<FlowPayload> flows;
127 private Set<FlowPathPayload> flowPaths =
new HashSet<>();
130 @Given(
"^flows defined over active switches in the reference topology$")
132 flows = flowManager.allActiveSwitchesFlows();
135 @Given(
"^flows defined over active traffgens in the reference topology$")
140 @Given(
"Create (\\d+) flows? with A Switch used and at least (\\d+) alternate paths? between source and " 141 +
"destination switch and (\\d+) bandwidth")
146 flows = flowIsls.keySet();
147 flows.forEach(flow -> flowPaths.add(northboundService.
getFlowPath(flow.getId())));
150 @And(
"^flow paths? (?:is|are) changed")
152 Set<FlowPathPayload> actualFlowPaths =
new HashSet<>();
153 flows.forEach(flow -> actualFlowPaths.add(northboundService.
getFlowPath(flow.getId())));
154 assertThat(actualFlowPaths, everyItem(not(isIn(flowPaths))));
157 flowPaths = actualFlowPaths;
160 @And(
"^flow paths? (?:is|are) unchanged")
162 Set<FlowPathPayload> actualFlowPaths =
new HashSet<>();
163 flows.forEach(flow -> actualFlowPaths.add(northboundService.
getFlowPath(flow.getId())));
164 assertThat(actualFlowPaths, everyItem(isIn(flowPaths)));
167 @And(
"Create defined flows?")
173 @And(
"^each flow has unique flow_id$")
175 flows.forEach(flow -> flow.setId(
format(
"%s-%s", flow.getId(), UUID.randomUUID().toString())));
178 @And(
"^each flow has flow_id with (.*) prefix$")
180 flows.forEach(flow -> flow.setId(
format(
"%s-%s", flowIdPrefix, flow.getId())));
183 @And(
"^(?:each )?flow has max bandwidth set to (\\d+)$")
185 flows.forEach(flow -> flow.setMaximumBandwidth(bandwidth));
188 @When(
"^initialize creation of given flows$")
192 assertThat(
format(
"A flow creation request for '%s' failed.", flow.getId()),
result,
193 reflectEquals(flow,
"lastUpdated",
"status"));
194 assertThat(
format(
"Flow status for '%s' was not set to '%s'. Received status: '%s'",
197 assertThat(
format(
"The flow '%s' is missing lastUpdated field", flow.getId()),
result,
198 hasProperty(
"lastUpdated", notNullValue()));
202 @Then(
"^each flow is created and stored in TopologyEngine$")
204 List<Flow> expextedFlows = flows.stream()
205 .map(flow ->
new Flow(flow.getId(),
206 flow.getMaximumBandwidth(),
207 flow.isIgnoreBandwidth(), 0,
208 flow.getDescription(), null,
209 flow.getSource().getSwitchDpId(),
210 flow.getDestination().getSwitchDpId(),
211 flow.getSource().getPortId(),
212 flow.getDestination().getPortId(),
213 flow.getSource().getVlanId(),
214 flow.getDestination().getVlanId(),
218 for (
Flow expectedFlow : expextedFlows) {
221 .get(() -> topologyEngineService.
getFlow(expectedFlow.getFlowId()));
223 assertNotNull(
format(
"The flow '%s' is missing in TopologyEngine.", expectedFlow.getFlowId()), flowPair);
224 assertThat(
format(
"The flow '%s' in TopologyEngine is different from defined.", expectedFlow.getFlowId()),
225 flowPair.
getLeft(), is(equalTo(expectedFlow)));
229 @And(
"^(?:each )?flow is in UP state$")
234 private void eachFlowIsUp(Set<FlowPayload> flows) {
240 assertNotNull(
format(
"The flow status for '%s' can't be retrived from Northbound.", flow.getId()),
status);
241 assertThat(
format(
"The flow '%s' in Northbound is different from defined.", flow.getId()),
242 status, hasProperty(
"id", equalTo(flow.getId())));
243 assertThat(
format(
"The flow '%s' has wrong status in Northbound.", flow.getId()),
248 @And(
"^each flow can be read from Northbound$")
253 assertNotNull(
format(
"The flow '%s' is missing in Northbound.", flow.getId()),
result);
254 assertEquals(
format(
"The flow '%s' in Northbound is different from defined.", flow.getId()), flow.getId(),
259 @And(
"^(?:each )?flow is valid per Northbound validation$")
261 flows.forEach(flow -> {
262 List<FlowValidationDto> validations = northboundService.
validateFlow(flow.getId());
263 validations.forEach(flowValidation -> {
264 assertEquals(flow.getId(), flowValidation.getFlowId());
265 assertTrue(
format(
"The flow '%s' has discrepancies: %s", flow.getId(),
266 flowValidation.getDiscrepancies()), flowValidation.getDiscrepancies().isEmpty());
267 assertTrue(
format(
"The flow '%s' didn't pass validation.", flow.getId()),
268 flowValidation.getAsExpected());
274 @And(
"^(?:each )?flow has traffic going with bandwidth not less than (\\d+) and not greater than (\\d+)$")
277 List<Exam> examsInProgress = buildAndStartTraffExams();
279 List<String> issues =
new ArrayList<>();
281 for (
Exam exam : examsInProgress) {
282 String flowId = exam.getFlow().getId();
285 softAssertions.checkThat(
format(
"The flow %s had errors: %s",
287 softAssertions.checkThat(
format(
"The flow %s had no traffic.", flowId),
289 softAssertions.checkThat(
format(
"The flow %s had unexpected bandwidth: %s", flowId, report.
getBandwidth()),
291 && report.
getBandwidth().getKbps() < bandwidthHighLimit, is(
true));
296 @And(
"^each flow has no traffic$")
298 List<Exam> examsInProgress = buildAndStartTraffExams();
300 List<ExamReport> hasTraffic = examsInProgress.stream()
301 .map(exam -> traffExam.
waitExam(exam))
305 assertThat(
"Detected unexpected traffic.", hasTraffic, empty());
308 private List<Exam> buildAndStartTraffExams() {
311 List<Exam>
result = flows.stream()
315 return Stream.of(examBuilder.
buildExam(flow, 0));
316 }
catch (FlowNotApplicableException ex) {
317 log.info(
"Skip traffic exam. {}", ex.getMessage());
318 return Stream.empty();
323 ExamResources resources = traffExam.
startExam(exam);
324 exam.setResources(resources);
325 }
catch (OperationalException ex) {
326 log.error(
"Unable to start traffic exam for {}.", exam.getFlow(), ex);
327 fail(ex.getMessage());
332 log.info(
"{} of {} flow's traffic examination have been started",
result.size(),
338 @Then(
"^each flow can be updated with (\\d+) max bandwidth( and new vlan)?$")
340 final boolean newVlan = newVlanStr != null;
342 flow.setMaximumBandwidth(bandwidth);
344 flow.getDestination().setVlanId(getAllowedVlan(flows, flow.getDestination().getSwitchDpId()));
345 flow.getSource().setVlanId(getAllowedVlan(flows, flow.getSource().getSwitchDpId()));
348 assertThat(
format(
"A flow update request for '%s' failed.", flow.getId()),
result,
349 reflectEquals(flow,
"lastUpdated",
"status"));
353 private int getAllowedVlan(Set<FlowPayload> flows,
SwitchId switchDpId) {
354 RangeSet<Integer> allocatedVlans = TreeRangeSet.create();
356 allocatedVlans.add(Range.singleton(f.getSource().getVlanId()));
357 allocatedVlans.add(Range.singleton(f.getDestination().getVlanId()));
359 RangeSet<Integer> availableVlansRange = TreeRangeSet.create();
360 Switch theSwitch = topologyDefinition.getSwitches().stream()
361 .filter(sw -> sw.getDpId().equals(switchDpId)).findFirst().get();
362 theSwitch.getOutPorts().forEach(
port -> availableVlansRange.addAll(
port.getVlanRange()));
363 availableVlansRange.removeAll(allocatedVlans);
364 return availableVlansRange.asRanges().stream()
365 .flatMap(range -> ContiguousSet.create(range, DiscreteDomain.integers()).stream())
369 @And(
"^each flow has meters installed with (\\d+) max bandwidth$")
377 int forwardMeterId = flowPair.
getLeft().getMeterId();
378 assertThat(forwardSwitchMeters, hasKey(forwardMeterId));
379 MeterEntry forwardMeter = forwardSwitchMeters.get(forwardMeterId);
380 assertThat(forwardMeter.getEntries(), contains(hasProperty(
"rate", equalTo(bandwidth))));
384 int reverseMeterId = flowPair.
getRight().getMeterId();
385 assertThat(reverseSwitchMeters, hasKey(reverseMeterId));
386 MeterEntry reverseMeter = reverseSwitchMeters.get(reverseMeterId);
387 assertThat(reverseMeter.getEntries(), contains(hasProperty(
"rate", equalTo(bandwidth))));
388 }
catch (UnsupportedOperationException ex) {
390 log.warn(
"Switch doesn't support dumping of meters. {}", ex.getMessage());
395 @And(
"^all active switches have no excessive meters installed$")
397 ListValuedMap<SwitchId, Integer> switchMeters =
new ArrayListValuedHashMap<>();
400 if (flowPair != null) {
401 switchMeters.put(flowPair.
getLeft().getSourceSwitch(), flowPair.
getLeft().getMeterId());
402 switchMeters.put(flowPair.
getRight().getSourceSwitch(), flowPair.
getRight().getMeterId());
407 switches.forEach(sw -> {
408 List<Integer> expectedMeters = switchMeters.get(sw.getDpId());
410 List<Integer> actualMeters = floodlightService.
getMeters(sw.getDpId()).values().stream()
411 .map(MeterEntry::getMeterId)
414 if (!expectedMeters.isEmpty() || !actualMeters.isEmpty()) {
415 assertThat(
format(
"Meters of switch %s don't match expected.", sw), actualMeters,
416 containsInAnyOrder(expectedMeters));
419 }
catch (UnsupportedOperationException ex) {
421 log.warn(
"Switch doesn't support dumping of meters. {}", ex.getMessage());
426 @Then(
"^each flow can be deleted$")
428 List<String> deletedFlowIds =
new ArrayList<>();
433 deletedFlowIds.add(
result.getId());
437 assertThat(
"Deleted flows from Northbound don't match expected", deletedFlowIds, containsInAnyOrder(
438 flows.stream().map(flow -> equalTo(flow.getId())).collect(toList())));
441 @And(
"^each flow can not be read from Northbound$")
446 .retryIf(Objects::nonNull))
447 .get(() -> northboundService.
getFlow(flow.getId()));
449 assertNull(
format(
"The flow '%s' exists.", flow.getId()),
result);
453 @And(
"^each flow can not be read from TopologyEngine$")
458 .retryIf(Objects::nonNull))
459 .get(() -> topologyEngineService.
getFlow(flow.getId()));
461 assertNull(
format(
"The flow '%s' exists.", flow.getId()),
result);
465 @And(
"^create flow between '(.*)' and '(.*)' and alias it as '(.*)'$")
467 Switch srcSwitch = topologyUnderTest.getAliasedObject(srcAlias);
468 Switch dstSwitch = topologyUnderTest.getAliasedObject(dstAlias);
470 srcSwitch, dstSwitch, 1000);
471 northboundService.
addFlow(flow);
472 topologyUnderTest.
addAlias(flowAlias, flow);
475 @And(
"^'(.*)' flow is in UP state$")
477 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
478 eachFlowIsUp(Collections.singleton(flow));
481 @When(
"^request all switch meters for switch '(.*)' and alias results as '(.*)'$")
483 Switch sw = topologyUnderTest.getAliasedObject(switchAlias);
484 topologyUnderTest.
addAlias(meterAlias, floodlightService.
getMeters(sw.getDpId()));
488 @And(
"^select first meter of '(.*)' and alias it as '(.*)'$")
491 Entry<Integer, MeterEntry> firstMeter = meters.entrySet().iterator().next();
492 topologyUnderTest.
addAlias(newMeterAlias, firstMeter);
495 @Then(
"^meters '(.*)' does not have '(.*)'$")
498 Entry<Integer, MeterEntry> meter = topologyUnderTest.getAliasedObject(meterAlias);
499 assertFalse(meters.containsKey(meter.getKey()));
502 private RetryPolicy retryPolicy() {
503 return new RetryPolicy()
504 .withDelay(2, TimeUnit.SECONDS)
508 @Given(
"^random flow aliased as '(.*)'$")
513 @And(
"^change bandwidth of (.*) flow to (\\d+)$")
515 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
519 @When(
"^change bandwidth of (.*) flow to '(.*)'$")
521 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
522 long bw = topologyUnderTest.getAliasedObject(bwAlias);
526 @And(
"^create flow '(.*)'$")
528 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
529 flowResponse = northboundService.
addFlow(flow);
532 @And(
"^get available bandwidth and maximum speed for flow (.*) and alias them as '(.*)' " 533 +
"and '(.*)' respectively$")
535 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
536 List<PathNodePayload> flowPath = northboundService.
getFlowPath(flow.
getId()).getForwardPath();
537 List<IslInfoData> allLinks = northboundService.
getAllLinks();
538 long minBw = Long.MAX_VALUE;
539 long minSpeed = Long.MAX_VALUE;
546 for (
int i = 1;
i < flowPath.size();
i++) {
550 link.getPath().get(0).getSwitchId().equals(from.getSwitchId())
551 && link.getPath().get(1).getSwitchId().equals(to.getSwitchId()))
553 minBw = Math.min(isl.getAvailableBandwidth(), minBw);
554 minSpeed = Math.min(isl.getSpeed(), minSpeed);
556 topologyUnderTest.
addAlias(bwAlias, minBw);
557 topologyUnderTest.
addAlias(speedAlias, minSpeed);
560 @And(
"^update flow (.*)$")
562 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
566 @When(
"^get info about flow (.*)$")
568 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
572 @Then(
"^response flow has bandwidth equal to '(.*)'$")
574 long expectedBw = topologyUnderTest.getAliasedObject(bwAlias);
578 @Then(
"^response flow has bandwidth equal to (\\d+)$")
583 @And(
"^delete flow (.*)$")
585 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
589 @And(
"^get path of '(.*)' and alias it as '(.*)'$")
591 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
595 @And(
"^(.*) flow's path equals to '(.*)'$")
597 FlowPayload flow = topologyUnderTest.getAliasedObject(flowAlias);
598 FlowPathPayload expectedPath = topologyUnderTest.getAliasedObject(pathAlias);
600 assertThat(actualPath, equalTo(expectedPath));
603 private String getTimestamp() {
604 return new SimpleDateFormat(
"ddMMMHHmm").format(
new Date());
Map< FlowPayload, List< TopologyDefinition.Isl > > createFlowsWithASwitch(int flowsAmount, int alternatePaths, int bandwidth)
void createFlow(String flowAlias)
void getPathAndAlias(String flowAlias, String pathAlias)
MetersEntriesMap getMeters(SwitchId dpid)
List< String > getErrors()
void setMaximumBandwidth(long maximumBandwidth)
List< Switch > getActiveSwitches()
void creationRequestForEachFlowIsSuccessful()
void flowsWithAlternatePaths(int flowsAmount, int alternatePaths, int bw)
void setUniqueFlowIdToEachFlow()
long getMaximumBandwidth()
void doesNotHaveMeter(String metersAlias, String meterAlias)
void responseFlowHasBandwidth(String bwAlias)
void getInfoAboutFlow(String flowAlias)
void getAvailableBandwidthAndSpeed(String flowAlias, String bwAlias, String speedAlias)
void defineFlowsOverActiveTraffgens()
void eachFlowCanNotBeReadFromNorthbound()
List< FlowValidationDto > validateFlow(String flowId)
void updateFlow(String flowAlias)
ExamReport waitExam(Exam exam)
FlowPayload getFlow(String flowId)
void randomFlowAliasedAsFlow(String flowAlias)
void setBandwidthToEachFlow(int bandwidth)
FlowPathPayload getFlowPath(String flowId)
void noExcessiveMetersInstalledOnActiveSwitches()
void createFlowBetween(String srcAlias, String dstAlias, String flowAlias)
void deleteFlow(String flowAlias)
void addAlias(String alias, Object obj)
void eachFlowHasTrafficGoingWithBandwidthNotLessThan(int bandwidthLowLimit, int bandwidthHighLimit)
FlowPayload addFlow(FlowPayload payload)
void buildFlowIdToEachFlow(String flowIdPrefix)
void eachFlowIsInUpState()
Set< FlowPayload > allActiveTraffgenFlows()
void flowPathIsUnchanged()
void flowIsUp(String flowAlias)
void eachFlowCanBeReadFromNorthbound()
ExamResources startExam(Exam exam)
void requestMeters(String switchAlias, String meterAlias)
void eachFlowHasNoTraffic()
void defineFlowsOverActiveSwitches()
void eachFlowHasMetersInstalledWithBandwidth(long bandwidth)
ImmutablePair< Flow, Flow > getFlow(String flowId)
void eachFlowCanBeUpdatedWithBandwidth(int bandwidth, String newVlanStr)
Exam buildExam(FlowPayload flow, int bandwidth)
FlowPayload buildWithAnyPortsInUniqueVlan(String flowId, Switch srcSwitch, Switch destSwitch, int bandwidth)
void selectFirstMeter(String metersAlias, String newMeterAlias)
void verifyFlowPath(String flowAlias, String pathAlias)
void eachFlowCanBeDeleted()
List< IslInfoData > getAllLinks()
FlowPayload updateFlow(String flowId, FlowPayload payload)
FlowPayload deleteFlow(String flowId)
void changeBandwidthOfFlow(String flowAlias, int newBw)
void eachFlowCanNotBeReadFromTopologyEngine()
void eachFlowIsCreatedAndStoredInTopologyEngine()
FlowIdStatusPayload getFlowStatus(String flowId)