Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

nbworker add feature to reroute y-flow for switch/link evacuation process #5751

Merged
merged 2 commits into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src-gui/ui/src/app/common/constants/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export const MessageObj = {
bfd_flag_updated: 'BFD flag updated successfully!',
flows_evacuated: 'All flows are evacuated successfully!',
error_flows_evacuated: 'Error in evacuating flows!',
info_cannot_evacuate_flows_from_switch: 'Can not evacuate flows while switch is not under maintenance.',
info_cannot_evacuate_flows_from_isl: 'Can not evacuate flows while ISL is not under maintenance.',
reverse_graph_no_data: 'Backward graph API did not return data.',
forward_graph_no_data: 'Forward graph API did not return data.',
updating_isl_bandwidth: 'Updating ISL max bandwidth',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,18 +242,13 @@ <h5 class='modal-title' style="margin-top: 0px !important">ISL DETAILS </h5>
</div>
</div>
<div class='row isl_sbl_details mt-3' *ngIf="commonService.hasPermission('isl_update_maintenance')">
<label class='col-sm-6 col-form-label'>Evacuate All Flows:
</label>
<label class='col-sm-6 col-form-label'>Evacuate All Flows:</label>
<div class='col-sm-6'>
<div class="pull-left">
<div class="onoffswitch">
<input type="checkbox" (change)="evacuateIsl($event)" name="isl-evacuate"
class="onoffswitch-checkbox" id="onoffevacuateisl" [checked]="evacuate">
<label class="onoffswitch-label" for="onoffevacuateisl">
<span class="onoffswitch-inner onoffswitch-inner-maintenance-switch"></span>
<span class="onoffswitch-switch" id="onoffswitch-switch"></span>
</label>
</div>
<button type="button" class="btn pull-left kilda_btn"
style="padding: 0px 9px!important;"
(click)="evacuateIsl($event)">Evacuate
</button> &nbsp;
</div>
</div>
</div>
Expand Down
19 changes: 6 additions & 13 deletions src-gui/ui/src/app/modules/isl/isl-detail/isl-detail.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ export class IslDetailComponent implements OnInit, AfterViewInit, OnDestroy {
state = '';
bfd_session_status = '';
enable_bfd = false;
evacuate = false;
under_maintenance = false;
loadingData = true;
isBFDEdit: any = false;
Expand Down Expand Up @@ -187,7 +186,6 @@ export class IslDetailComponent implements OnInit, AfterViewInit, OnDestroy {
this.bfd_session_status = retrievedObject.bfd_session_status;
this.available_bandwidth = retrievedObject.available_bandwidth;
this.under_maintenance = retrievedObject.under_maintenance;
this.evacuate = retrievedObject.evacuate;
this.enable_bfd = retrievedObject.enable_bfd;
this.clipBoardItems = Object.assign(this.clipBoardItems, {
sourceSwitchName: retrievedObject.source_switch_name,
Expand Down Expand Up @@ -413,14 +411,13 @@ export class IslDetailComponent implements OnInit, AfterViewInit, OnDestroy {
}

evacuateIsl(e) {
if (!this.under_maintenance) {
this.toastr.info(MessageObj.info_cannot_evacuate_flows_from_isl, 'Can not evacuate');
return;
}
const modalRef = this.modalService.open(ModalconfirmationComponent);
modalRef.componentInstance.title = 'Confirmation';
this.evacuate = e.target.checked;
if (this.evacuate) {
modalRef.componentInstance.content = 'Are you sure you want to evacuate all flows?';
} else {
modalRef.componentInstance.content = 'Are you sure ?';
}
modalRef.componentInstance.content = 'Are you sure you want to evacuate all flows?';
modalRef.result.then((response) => {
if (response && response == true) {
const data = {
Expand All @@ -429,19 +426,15 @@ export class IslDetailComponent implements OnInit, AfterViewInit, OnDestroy {
dst_switch: this.dst_switch,
dst_port: this.dst_port,
under_maintenance: this.under_maintenance,
evacuate: e.target.checked
evacuate: true
};
this.islListService.islUnderMaintenance(data).subscribe(response => {
this.toastr.success(MessageObj.flows_evacuated, 'Success');
location.reload();
}, error => {
this.toastr.error(MessageObj.error_flows_evacuated, 'Error');
});
} else {
this.evacuate = false;
}
}, error => {
this.evacuate = false;
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,10 @@ <h5 class="modal-title mb-2">SWITCH DETAILS
<label class="col-sm-4 col-form-label">Evacuate:</label>
<div class="col-sm-4 switchdetails_div_address col-form-label">
<div class="onoffswitch">
<input type="checkbox" (change)="evacuateSwitch($event)" name="onoffswitchevacuate"
class="onoffswitch-checkbox" id="onoffswitchevacuate" [checked]="evacuate">
<label class="onoffswitch-label" for="onoffswitchevacuate">
<span class="onoffswitch-inner onoffswitch-inner-maintenance-switch"></span>
<span class="onoffswitch-switch"></span>
</label>
<button type="button" class="btn pull-left kilda_btn"
style="padding: 0px 9px!important; margin: 0px 0px -10px 0px;"
(click)="evacuateSwitch($event)">Evacuate
</button> &nbsp;
</div>
</div>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,31 +442,24 @@ export class SwitchDetailComponent implements OnInit, AfterViewInit, OnDestroy {
}

evacuateSwitch(e) {
if (!this.underMaintenance) {
this.toastr.info(MessageObj.info_cannot_evacuate_flows_from_switch, 'Can not evacuate');
return;
}
const modalRef = this.modalService.open(ModalconfirmationComponent);
modalRef.componentInstance.title = 'Confirmation';
this.evacuate = e.target.checked;
if (this.evacuate) {
modalRef.componentInstance.content = 'Are you sure you want to evacuate all flows?';
} else {
modalRef.componentInstance.content = 'Are you sure ?';
}
modalRef.componentInstance.content = 'Are you sure you want to evacuate all flows?';
modalRef.result.then((response) => {
if (response && response == true) {
const data = {'under_maintenance': this.underMaintenance, 'evacuate': e.target.checked};
const data = {'under_maintenance': this.underMaintenance, 'evacuate': true};
this.switchService.switchMaintenance(data, this.switchId).subscribe((serverResponse) => {
this.toastr.success(MessageObj.flows_evacuated, 'Success');
location.reload();
}, error => {
this.toastr.error(MessageObj.error_flows_evacuated, 'Error');
});
} else {
this.evacuate = false;
}
}, error => {
this.evacuate = false;
});


}

ngOnDestroy() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -88,13 +88,14 @@ public FlowPath(@NonNull PathId pathId, @NonNull Switch srcSwitch, @NonNull Swit
long latency, long bandwidth,
boolean ignoreBandwidth, FlowPathStatus status, List<PathSegment> segments,
Set<FlowApplication> applications,
String sharedBandwidthGroupId, HaFlowPath haFlowPath) {
String sharedBandwidthGroupId, HaFlowPath haFlowPath, Flow flow) {
data = FlowPathDataImpl.builder().pathId(pathId).srcSwitch(srcSwitch).destSwitch(destSwitch)
.cookie(cookie).meterId(meterId).ingressMirrorGroupId(ingressMirrorGroupId)
.latency(latency).bandwidth(bandwidth)
.ignoreBandwidth(ignoreBandwidth).status(status)
.applications(applications)
.sharedBandwidthGroupId(sharedBandwidthGroupId).haFlowPath(haFlowPath)
.flow(flow)
.build();
// The reference is used to link path segments back to the path. See {@link #setSegments(List)}.
((FlowPathDataImpl) data).flowPath = this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

import org.openkilda.messaging.Message;
import org.openkilda.messaging.MessageData;
import org.openkilda.messaging.command.flow.FlowRerouteRequest;
import org.openkilda.messaging.command.BaseRerouteRequest;
import org.openkilda.messaging.command.switches.SwitchValidateRequest;
import org.openkilda.messaging.error.ErrorData;
import org.openkilda.wfm.CommandContext;
Expand All @@ -38,7 +38,7 @@ protected void handleInput(Tuple input) throws Exception {
CommandContext commandContext = pullContext(input);
Message message = wrap(commandContext, payload);

if (payload instanceof FlowRerouteRequest) {
if (payload instanceof BaseRerouteRequest) {
getOutput().emit(input.getSourceStreamId(), input, new Values(message));
} else if (payload instanceof SwitchValidateRequest) {
getOutput().emit(input.getSourceStreamId(), input,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.openkilda.messaging.command.flow.FlowRequest;
import org.openkilda.messaging.command.flow.FlowRerouteRequest;
import org.openkilda.messaging.command.haflow.HaFlowRerouteRequest;
import org.openkilda.messaging.command.yflow.YFlowRerouteRequest;
import org.openkilda.messaging.error.ErrorType;
import org.openkilda.messaging.error.InvalidFlowException;
import org.openkilda.messaging.error.MessageException;
Expand Down Expand Up @@ -83,6 +84,7 @@
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import net.jodah.failsafe.RetryPolicy;
import org.apache.commons.lang3.StringUtils;

import java.time.Duration;
import java.time.Instant;
Expand Down Expand Up @@ -713,10 +715,20 @@ public List<BaseRerouteRequest> makeRerouteRequests(
}
}
} else {
if (processed.add(flow.getFlowId())) {
FlowRerouteRequest request = new FlowRerouteRequest(
flow.getFlowId(), false, false, affectedIslEndpoints, reason, false);
results.add(request);
if (StringUtils.isNotBlank(flow.getYFlowId())) {
if (yFlowRepository.exists(flow.getYFlowId())) {
if (processed.add(flow.getYFlowId())) {
YFlowRerouteRequest req = new YFlowRerouteRequest(flow.getYFlowId(), affectedIslEndpoints,
reason, false);
results.add(req);
}
}
} else {
if (processed.add(flow.getFlowId())) {
FlowRerouteRequest request = new FlowRerouteRequest(
flow.getFlowId(), false, false, affectedIslEndpoints, reason, false);
results.add(request);
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;

import org.openkilda.messaging.command.BaseRerouteRequest;
import org.openkilda.messaging.command.flow.FlowRequest;
import org.openkilda.messaging.command.flow.FlowRerouteRequest;
import org.openkilda.messaging.command.yflow.YFlowRerouteRequest;
import org.openkilda.messaging.error.InvalidFlowException;
import org.openkilda.messaging.info.InfoData;
import org.openkilda.messaging.model.FlowPatch;
Expand All @@ -39,12 +42,15 @@
import org.openkilda.model.Switch;
import org.openkilda.model.SwitchId;
import org.openkilda.model.SwitchStatus;
import org.openkilda.model.YFlow;
import org.openkilda.model.YFlow.SharedEndpoint;
import org.openkilda.model.cookie.FlowSegmentCookie;
import org.openkilda.persistence.inmemory.InMemoryGraphBasedTest;
import org.openkilda.persistence.repositories.FlowPathRepository;
import org.openkilda.persistence.repositories.FlowRepository;
import org.openkilda.persistence.repositories.PathSegmentRepository;
import org.openkilda.persistence.repositories.SwitchRepository;
import org.openkilda.persistence.repositories.YFlowRepository;
import org.openkilda.wfm.error.FlowNotFoundException;
import org.openkilda.wfm.error.SwitchNotFoundException;
import org.openkilda.wfm.share.flow.TestFlowBuilder;
Expand All @@ -71,6 +77,7 @@ public class FlowOperationsServiceTest extends InMemoryGraphBasedTest {
private static final String FLOW_ID_1 = "flow_1";
private static final String FLOW_ID_2 = "flow_2";
private static final String FLOW_ID_3 = "flow_3";
private static final String Y_FLOW_ID_1 = "y_flow_1";
private static final PathId FORWARD_PATH_1 = new PathId("forward_path_1");
private static final PathId FORWARD_PATH_2 = new PathId("forward_path_2");
private static final PathId FORWARD_PATH_3 = new PathId("forward_path_3");
Expand All @@ -91,6 +98,7 @@ public class FlowOperationsServiceTest extends InMemoryGraphBasedTest {

private static FlowOperationsService flowOperationsService;
private static FlowRepository flowRepository;
private static YFlowRepository yFlowRepository;
private static FlowPathRepository flowPathRepository;
private static PathSegmentRepository pathSegmentRepository;
private static SwitchRepository switchRepository;
Expand All @@ -103,6 +111,7 @@ public class FlowOperationsServiceTest extends InMemoryGraphBasedTest {
@BeforeAll
public static void setUpOnce() {
flowRepository = persistenceManager.getRepositoryFactory().createFlowRepository();
yFlowRepository = persistenceManager.getRepositoryFactory().createYFlowRepository();
flowPathRepository = persistenceManager.getRepositoryFactory().createFlowPathRepository();
pathSegmentRepository = persistenceManager.getRepositoryFactory().createPathSegmentRepository();
switchRepository = persistenceManager.getRepositoryFactory().createSwitchRepository();
Expand Down Expand Up @@ -789,7 +798,7 @@ public void whenFlowWithMaxLatency_patchFlowWithLatencyTier2OnlyTest()
}

@Test
void whenPartialUpdate_dumpBeforeAndDumpAfterIsSaved() throws FlowNotFoundException, InvalidFlowException {
public void whenPartialUpdate_dumpBeforeAndDumpAfterIsSaved() throws FlowNotFoundException, InvalidFlowException {
Flow createdFlow = createFlow(FLOW_ID_1, switchA, 1, switchC, 2,
FORWARD_PATH_1, REVERSE_PATH_1, switchB, false,
100_500L, 0L);
Expand All @@ -815,7 +824,7 @@ void whenPartialUpdate_dumpBeforeAndDumpAfterIsSaved() throws FlowNotFoundExcept
}

@Test
void whenFullUpdateIsRequired_historyActionIsSaved() throws FlowNotFoundException, InvalidFlowException {
public void whenFullUpdateIsRequired_historyActionIsSaved() throws FlowNotFoundException, InvalidFlowException {
Flow createdFlow = createFlow(FLOW_ID_1, switchA, 1, switchC, 2,
FORWARD_PATH_1, REVERSE_PATH_1, switchB, false,
100_500L, 0L);
Expand All @@ -839,6 +848,91 @@ void whenFullUpdateIsRequired_historyActionIsSaved() throws FlowNotFoundExceptio
assertEquals(action, carrier.getHistoryHolderList().get(2).getFlowHistoryData().getAction());
}

@Test
public void makeRerouteRequests() {
YFlow yFlow = buildYFlow(Y_FLOW_ID_1, switchA, 1, switchD);
yFlowRepository.add(yFlow);

Flow ySubflow1 = buildFlow(null, Y_FLOW_ID_1, switchA, 1, 10,
switchB, 2, 11, "subFlow1", yFlow);
FlowPath yFlowForwardPath1 = FlowPath.builder()
.pathId(new PathId("subPath1"))
.srcSwitch(switchA)
.destSwitch(switchB)
.flow(ySubflow1)
.build();

Flow ySubflow2 = buildFlow(null, Y_FLOW_ID_1, switchA, 1, 20,
switchC, 2, 22, "subFlow2", yFlow);
FlowPath yFlowForwardPath2 = FlowPath.builder()
.pathId(new PathId("subPath2"))
.srcSwitch(switchA)
.destSwitch(switchB)
.flow(ySubflow2)
.build();

Flow flow1 = buildFlow(FLOW_ID_1, null, switchA, 1, 100, switchB,
2, 111, "regularFlow", null);
FlowPath flowPath1 = FlowPath.builder()
.pathId(new PathId("path1"))
.srcSwitch(switchA)
.destSwitch(switchB)
.flow(flow1)
.build();

List<FlowPath> flowPaths = Arrays.asList(yFlowForwardPath1, flowPath1, yFlowForwardPath2);

// 3 flow path: 1 for regular flow and 2 for y-flow
List<BaseRerouteRequest> actualResult =
flowOperationsService.makeRerouteRequests(flowPaths, new HashSet<>(), "Great reason to reroute");

Assertions.assertEquals(2, actualResult.size());
Assertions.assertInstanceOf(YFlowRerouteRequest.class, actualResult.get(0));
Assertions.assertEquals(Y_FLOW_ID_1, actualResult.get(0).getFlowId());
Assertions.assertInstanceOf(FlowRerouteRequest.class, actualResult.get(1));
Assertions.assertEquals(FLOW_ID_1, actualResult.get(1).getFlowId());

// y-flow does not exist in the repository
transactionManager.doInTransaction(() -> yFlowRepository.remove(yFlow));
actualResult = flowOperationsService.makeRerouteRequests(flowPaths, new HashSet<>(), "Great reason to reroute");
Assertions.assertEquals(1, actualResult.size());
Assertions.assertInstanceOf(FlowRerouteRequest.class, actualResult.get(0));
Assertions.assertEquals(FLOW_ID_1, actualResult.get(0).getFlowId());
}

private YFlow buildYFlow(String yFlowId, Switch sharedEndpoint, int portNumber, Switch yPoint) {
return YFlow.builder()
.yFlowId(yFlowId)
.sharedEndpoint(new SharedEndpoint(sharedEndpoint.getSwitchId(), portNumber))
.yPoint(yPoint.getSwitchId())
.status(FlowStatus.UP)
.build();
}

private Flow buildFlow(String flowId, String yflowId, Switch srcSwitch, int srcPort, int srcVlan, Switch destSwitch,
int destPort, int destVlan, String desc, YFlow yflow) {
TestFlowBuilder builder = new TestFlowBuilder()
.yFlow(yflow)
.srcSwitch(srcSwitch)
.srcPort(srcPort)
.srcVlan(srcVlan)
.destSwitch(destSwitch)
.destPort(destPort)
.destVlan(destVlan)
.encapsulationType(FlowEncapsulationType.TRANSIT_VLAN)
.pathComputationStrategy(PathComputationStrategy.COST)
.description(desc)
.status(FlowStatus.UP);

if (flowId != null) {
builder.flowId(flowId);
}
if (yflowId != null) {
builder.yFlowId(yflowId);
}
return builder.build();
}

private void assertFlows(Collection<Flow> actualFlows, String... expectedFlowIds) {
assertEquals(expectedFlowIds.length, actualFlows.size());
assertEquals(new HashSet<>(Arrays.asList(expectedFlowIds)),
Expand Down
Loading
Loading