diff --git a/docker/db-migration/migrations/024-add-lacp-partner-class.yaml b/docker/db-migration/migrations/024-add-lacp-partner-class.yaml new file mode 100644 index 00000000000..8429ef35bc8 --- /dev/null +++ b/docker/db-migration/migrations/024-add-lacp-partner-class.yaml @@ -0,0 +1,34 @@ +databaseChangeLog: + - changeSet: + id: tag + author: dkakollu + changes: + - tagDatabase: + tag: 024-add-lacp-partner-class + + - changeSet: + id: add-lacp-partner-class + author: dkakollu + changes: + - sql: "CREATE CLASS lacp_partner IF NOT EXISTS EXTENDS V" + - sql: "CREATE PROPERTY lacp_partner.switch_id IF NOT EXISTS STRING" + - sql: "CREATE PROPERTY lacp_partner.logical_port_number IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.system_priority IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.system_id IF NOT EXISTS STRING" + - sql: "CREATE PROPERTY lacp_partner.key IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.port_priority IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.port_number IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.state_active IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_short_timeout IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_aggregatable IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_synchronised IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_collecting IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_distributing IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_defaulted IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_expired IF NOT EXISTS BOOLEAN" + - sql: "CREATE INDEX lacp_partner.switch_id IF NOT EXISTS NOTUNIQUE_HASH_INDEX" + - sql: "CREATE INDEX lacp_partner.logical_port_number IF NOT EXISTS NOTUNIQUE_HASH_INDEX" + + rollback: + - sql: "DELETE VERTEX lacp_partner" + - sql: "DROP CLASS lacp_partner IF EXISTS" diff --git a/docker/db-migration/migrations/026-add-lacp-partner-class.yaml b/docker/db-migration/migrations/026-add-lacp-partner-class.yaml new file mode 100644 index 00000000000..8429ef35bc8 --- /dev/null +++ b/docker/db-migration/migrations/026-add-lacp-partner-class.yaml @@ -0,0 +1,34 @@ +databaseChangeLog: + - changeSet: + id: tag + author: dkakollu + changes: + - tagDatabase: + tag: 024-add-lacp-partner-class + + - changeSet: + id: add-lacp-partner-class + author: dkakollu + changes: + - sql: "CREATE CLASS lacp_partner IF NOT EXISTS EXTENDS V" + - sql: "CREATE PROPERTY lacp_partner.switch_id IF NOT EXISTS STRING" + - sql: "CREATE PROPERTY lacp_partner.logical_port_number IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.system_priority IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.system_id IF NOT EXISTS STRING" + - sql: "CREATE PROPERTY lacp_partner.key IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.port_priority IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.port_number IF NOT EXISTS INTEGER" + - sql: "CREATE PROPERTY lacp_partner.state_active IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_short_timeout IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_aggregatable IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_synchronised IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_collecting IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_distributing IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_defaulted IF NOT EXISTS BOOLEAN" + - sql: "CREATE PROPERTY lacp_partner.state_expired IF NOT EXISTS BOOLEAN" + - sql: "CREATE INDEX lacp_partner.switch_id IF NOT EXISTS NOTUNIQUE_HASH_INDEX" + - sql: "CREATE INDEX lacp_partner.logical_port_number IF NOT EXISTS NOTUNIQUE_HASH_INDEX" + + rollback: + - sql: "DELETE VERTEX lacp_partner" + - sql: "DROP CLASS lacp_partner IF EXISTS" diff --git a/docker/db-migration/migrations/root.yaml b/docker/db-migration/migrations/root.yaml index 12202b78e5a..642b7265043 100644 --- a/docker/db-migration/migrations/root.yaml +++ b/docker/db-migration/migrations/root.yaml @@ -87,3 +87,6 @@ databaseChangeLog: - include: relativeToChangelogFile: true file: 025-optimise-switch-connected-device-class.yaml + - include: + relativeToChangelogFile: true + file: 026-add-lacp-partner-class.yaml diff --git a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpInfoData.java b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpInfoData.java new file mode 100644 index 00000000000..e19872ce7fd --- /dev/null +++ b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpInfoData.java @@ -0,0 +1,39 @@ +/* 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.messaging.info.event; + +import org.openkilda.messaging.info.InfoData; +import org.openkilda.model.SwitchId; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + + + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +public class LacpInfoData extends InfoData { + SwitchId switchId; + int logicalPortNumber; + LacpPartner actor; +} diff --git a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpPartner.java b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpPartner.java new file mode 100644 index 00000000000..b098a86f6b5 --- /dev/null +++ b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpPartner.java @@ -0,0 +1,41 @@ +/* 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.messaging.info.event; + +import org.openkilda.model.MacAddress; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +public class LacpPartner implements Serializable { + int systemPriority; + MacAddress systemId; + int key; + int portPriority; + int portNumber; + LacpState state; +} diff --git a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpState.java b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpState.java new file mode 100644 index 00000000000..165ab96ec0f --- /dev/null +++ b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/event/LacpState.java @@ -0,0 +1,41 @@ +/* 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.messaging.info.event; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.io.Serializable; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +public class LacpState implements Serializable { + boolean active; + boolean shortTimeout; + boolean aggregatable; + boolean synchronised; + boolean collecting; + boolean distributing; + boolean defaulted; + boolean expired; +} diff --git a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/ConnectedDevicesTopology.java b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/ConnectedDevicesTopology.java index 414d09d8786..2c5e48efee0 100644 --- a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/ConnectedDevicesTopology.java +++ b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/ConnectedDevicesTopology.java @@ -31,6 +31,7 @@ public class ConnectedDevicesTopology extends AbstractTopology { public static final String CONNECTED_DEVICES_SPOUT_ID = "connected-devices-spout"; + public static final String LACP_SPOUT_ID = "lacp-spout"; public static final String ROUTER_BOLT_ID = "router-bolt"; public static final String PACKET_BOLT_ID = "packet-bolt"; @@ -43,11 +44,13 @@ public ConnectedDevicesTopology(LaunchEnvironment env) { */ public StormTopology createTopology() { TopologyBuilder builder = new TopologyBuilder(); - PersistenceManager persistenceManager = new PersistenceManager(configurationProvider); createZkSpout(builder); createSpout(builder); + createLacpSpout(builder); + + PersistenceManager persistenceManager = new PersistenceManager(configurationProvider); createRouterBolt(builder, persistenceManager); createPacketBolt(builder, persistenceManager); @@ -66,6 +69,7 @@ private void createRouterBolt(TopologyBuilder builder, PersistenceManager persis RouterBolt routerBolt = new RouterBolt(persistenceManager, ZooKeeperSpout.SPOUT_ID); declareBolt(builder, routerBolt, ROUTER_BOLT_ID) .shuffleGrouping(CONNECTED_DEVICES_SPOUT_ID) + .shuffleGrouping(LACP_SPOUT_ID) .allGrouping(ZooKeeperSpout.SPOUT_ID); } @@ -80,6 +84,10 @@ private void createSpout(TopologyBuilder builder) { declareKafkaSpout(builder, topologyConfig.getKafkaTopoConnectedDevicesTopic(), CONNECTED_DEVICES_SPOUT_ID); } + private void createLacpSpout(TopologyBuilder builder) { + declareKafkaSpout(builder, topologyConfig.getKafkaLacpTopic(), LACP_SPOUT_ID); + } + private void createZkBolt(TopologyBuilder builder) { ZooKeeperBolt zooKeeperBolt = new ZooKeeperBolt(getConfig().getBlueGreenMode(), getZkTopoName(), getZookeeperConfig(), getBoltInstancesCount(ROUTER_BOLT_ID)); diff --git a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/ConnectedDevicesTopologyConfig.java b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/ConnectedDevicesTopologyConfig.java index 0cc298b2c90..758bd380553 100644 --- a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/ConnectedDevicesTopologyConfig.java +++ b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/ConnectedDevicesTopologyConfig.java @@ -25,4 +25,9 @@ public interface ConnectedDevicesTopologyConfig extends AbstractTopologyConfig { default String getKafkaTopoConnectedDevicesTopic() { return getKafkaTopics().getTopoConnectedDevicesTopic(); } + + default String getKafkaLacpTopic() { + return getKafkaTopics().getLacpTopic(); + } + } diff --git a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/bolts/PacketBolt.java b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/bolts/PacketBolt.java index 6c804c92d7e..1b0fd95ebe2 100644 --- a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/bolts/PacketBolt.java +++ b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/bolts/PacketBolt.java @@ -17,8 +17,9 @@ import static org.openkilda.wfm.topology.utils.KafkaRecordTranslator.FIELD_ID_PAYLOAD; +import org.openkilda.messaging.info.InfoData; import org.openkilda.messaging.info.event.ArpInfoData; -import org.openkilda.messaging.info.event.ConnectedDevicePacketBase; +import org.openkilda.messaging.info.event.LacpInfoData; import org.openkilda.messaging.info.event.LldpInfoData; import org.openkilda.persistence.PersistenceManager; import org.openkilda.wfm.AbstractBolt; @@ -43,12 +44,14 @@ protected void init() { @Override protected void handleInput(Tuple input) throws PipelineException { - ConnectedDevicePacketBase data = pullValue(input, FIELD_ID_PAYLOAD, ConnectedDevicePacketBase.class); + InfoData data = pullValue(input, FIELD_ID_PAYLOAD, InfoData.class); if (data instanceof LldpInfoData) { packetService.handleLldpData((LldpInfoData) data); } else if (data instanceof ArpInfoData) { packetService.handleArpData((ArpInfoData) data); + } else if (data instanceof LacpInfoData) { + packetService.handleLacpData((LacpInfoData) data); } else { unhandledInput(input); } diff --git a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/bolts/RouterBolt.java b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/bolts/RouterBolt.java index 03a3b7cfc9b..92695d65df5 100644 --- a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/bolts/RouterBolt.java +++ b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/bolts/RouterBolt.java @@ -22,6 +22,7 @@ import org.openkilda.messaging.info.InfoData; import org.openkilda.messaging.info.InfoMessage; import org.openkilda.messaging.info.event.ArpInfoData; +import org.openkilda.messaging.info.event.LacpInfoData; import org.openkilda.messaging.info.event.LldpInfoData; import org.openkilda.persistence.PersistenceManager; import org.openkilda.wfm.AbstractBolt; @@ -57,8 +58,8 @@ protected void handleInput(Tuple input) throws PipelineException { emitWithContext(PACKET_STREAM_ID, input, new Values(createMessageKey((LldpInfoData) data), data)); } else if (data instanceof ArpInfoData) { emitWithContext(PACKET_STREAM_ID, input, new Values(createMessageKey((ArpInfoData) data), data)); - } else { - unhandledInput(input); + } else if (data instanceof LacpInfoData) { + emitWithContext(PACKET_STREAM_ID, input, new Values(createMessageKey((LacpInfoData) data), data)); } } else { unhandledInput(input); diff --git a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/service/PacketService.java b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/service/PacketService.java index 9d07d47190b..f9024539775 100644 --- a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/service/PacketService.java +++ b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/main/java/org/openkilda/wfm/topology/connecteddevices/service/PacketService.java @@ -15,7 +15,6 @@ package org.openkilda.wfm.topology.connecteddevices.service; -import static java.lang.String.format; import static org.openkilda.model.ConnectedDeviceType.ARP; import static org.openkilda.model.ConnectedDeviceType.LLDP; import static org.openkilda.model.SwitchConnectedDevice.buildUniqueArpIndex; @@ -33,16 +32,20 @@ import static org.openkilda.model.cookie.Cookie.LLDP_POST_INGRESS_VXLAN_COOKIE; import static org.openkilda.model.cookie.Cookie.LLDP_TRANSIT_COOKIE; +import org.openkilda.messaging.info.InfoData; import org.openkilda.messaging.info.event.ArpInfoData; import org.openkilda.messaging.info.event.ConnectedDevicePacketBase; +import org.openkilda.messaging.info.event.LacpInfoData; import org.openkilda.messaging.info.event.LldpInfoData; import org.openkilda.model.Flow; +import org.openkilda.model.LacpPartner; import org.openkilda.model.Switch; import org.openkilda.model.SwitchConnectedDevice; import org.openkilda.model.SwitchId; import org.openkilda.model.TransitVlan; import org.openkilda.persistence.PersistenceManager; import org.openkilda.persistence.repositories.FlowRepository; +import org.openkilda.persistence.repositories.LacpPartnerRepository; import org.openkilda.persistence.repositories.SwitchConnectedDeviceRepository; import org.openkilda.persistence.repositories.SwitchRepository; import org.openkilda.persistence.repositories.TransitVlanRepository; @@ -66,8 +69,8 @@ public class PacketService { private final SwitchConnectedDeviceRepository switchConnectedDeviceRepository; private final TransitVlanRepository transitVlanRepository; private final FlowRepository flowRepository; - private final Map switchConnectedDeviceCache = new HashMap<>(); + private final LacpPartnerRepository lacpPartnerRepository; public PacketService(PersistenceManager persistenceManager) { transactionManager = persistenceManager.getTransactionManager(); @@ -76,6 +79,33 @@ public PacketService(PersistenceManager persistenceManager) { .createSwitchConnectedDeviceRepository(); transitVlanRepository = persistenceManager.getRepositoryFactory().createTransitVlanRepository(); flowRepository = persistenceManager.getRepositoryFactory().createFlowRepository(); + lacpPartnerRepository = persistenceManager.getRepositoryFactory().createLacpPartnerRepository(); + } + + /** + * This key is needed to balance load on Packet Bolt. If you see that some packet bolts have high load, and + * some have low load, try to extend this key. Maximum extension is equal to + * SwitchConnectedDeviceFrame.UNIQUE_INDEX_PROPERTY + */ + public static String createMessageKey(LldpInfoData data) { + return String.format("%s_%s_lldp", data.getSwitchId(), data.getPortNumber()); + } + + /** + * This key is needed to balance load on Packet Bolt. If you see that some packet bolts have high load, and + * some have low load, try to extend this key. Maximum extension is equal to + * SwitchConnectedDeviceFrame.UNIQUE_INDEX_PROPERTY + */ + public static String createMessageKey(ArpInfoData data) { + return String.format("%s_%s_arp", data.getSwitchId(), data.getPortNumber()); + } + + /** + * This key is needed to balance load on Packet Bolt. If you see that some packet bolts have high load, and + * some have low load, try to extend this key. Maximum extension is equal to + */ + public static String createMessageKey(LacpInfoData data) { + return String.format("%s_%s_lacp", data.getSwitchId(), data.getLogicalPortNumber()); } /** @@ -131,25 +161,50 @@ public void handleArpData(ArpInfoData data) { createArpDevice(data, flowRelatedData); } } - - /** - * This key is needed to balance load on Packet Bolt. If you see that some packet bolts have high load, and - * some have low load, try to extend this key. Maximum extension is equal to - * SwitchConnectedDeviceFrame.UNIQUE_INDEX_PROPERTY - */ - public static String createMessageKey(LldpInfoData data) { - return format("%s_%s_%s_%s_%s_lldp", data.getSwitchId(), data.getPortNumber(), data.getMacAddress(), - data.getChassisId(), data.getPortId()); - } - + /** - * This key is needed to balance load on Packet Bolt. If you see that some packet bolts have high load, and - * some have low load, try to extend this key. Maximum extension is equal to - * SwitchConnectedDeviceFrame.UNIQUE_INDEX_PROPERTY + * Handle Lacp info data. */ - public static String createMessageKey(ArpInfoData data) { - return format("%s_%s_%s_%s_arp", data.getSwitchId(), data.getPortNumber(), data.getMacAddress(), - data.getIpAddress()); + public void handleLacpData(LacpInfoData data) { + transactionManager.doInTransaction(() -> { + Optional lacpPartnerOptional = lacpPartnerRepository.findBySwitchIdAndLogicalPortNumber( + data.getSwitchId(), data.getLogicalPortNumber()); + org.openkilda.messaging.info.event.LacpPartner actor = data.getActor(); + if (lacpPartnerOptional.isPresent()) { + lacpPartnerOptional.get().setSystemPriority(actor.getSystemPriority()); + lacpPartnerOptional.get().setSystemId(actor.getSystemId()); + lacpPartnerOptional.get().setKey(actor.getKey()); + lacpPartnerOptional.get().setPortPriority(actor.getPortPriority()); + lacpPartnerOptional.get().setPortNumber(actor.getPortNumber()); + lacpPartnerOptional.get().setStateActive(actor.getState().isActive()); + lacpPartnerOptional.get().setStateShortTimeout(actor.getState().isActive()); + lacpPartnerOptional.get().setStateAggregatable(actor.getState().isActive()); + lacpPartnerOptional.get().setStateSynchronised(actor.getState().isActive()); + lacpPartnerOptional.get().setStateCollecting(actor.getState().isActive()); + lacpPartnerOptional.get().setStateDistributing(actor.getState().isActive()); + lacpPartnerOptional.get().setStateDefaulted(actor.getState().isActive()); + lacpPartnerOptional.get().setStateExpired(actor.getState().isActive()); + } else { + LacpPartner partner = LacpPartner.builder() + .switchId(data.getSwitchId()) + .logicalPortNumber(data.getLogicalPortNumber()) + .systemPriority(actor.getSystemPriority()) + .systemId(actor.getSystemId()) + .key(actor.getKey()) + .portPriority(actor.getPortPriority()) + .portNumber(actor.getPortNumber()) + .stateActive(actor.getState().isActive()) + .stateShortTimeout(actor.getState().isActive()) + .stateAggregatable(actor.getState().isActive()) + .stateSynchronised(actor.getState().isActive()) + .stateCollecting(actor.getState().isActive()) + .stateDistributing(actor.getState().isActive()) + .stateDefaulted(actor.getState().isActive()) + .stateExpired(actor.getState().isActive()) + .build(); + lacpPartnerRepository.add(partner); + } + }); } private FlowRelatedData findFlowRelatedData(ConnectedDevicePacketBase data) { @@ -448,11 +503,13 @@ private void createArpDevice(ArpInfoData data, FlowRelatedData flowRelatedData) switchConnectedDeviceCache.put(buildArpUniqueIndex(data, vlan), connectedDevice); } - private String getPacketName(ConnectedDevicePacketBase data) { + private String getPacketName(InfoData data) { if (data instanceof LldpInfoData) { return "LLDP"; } else if (data instanceof ArpInfoData) { return "ARP"; + } else if (data instanceof LacpInfoData) { + return "LACP"; } else { return "unknown"; } diff --git a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/test/java/org/openkilda/wfm/topology/connecteddevices/service/PacketServiceTest.java b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/test/java/org/openkilda/wfm/topology/connecteddevices/service/PacketServiceTest.java index f9e3f8faf4a..6c7add3b7c2 100644 --- a/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/test/java/org/openkilda/wfm/topology/connecteddevices/service/PacketServiceTest.java +++ b/src-java/connecteddevices-topology/connecteddevices-storm-topology/src/test/java/org/openkilda/wfm/topology/connecteddevices/service/PacketServiceTest.java @@ -23,8 +23,12 @@ import static org.openkilda.model.cookie.Cookie.LLDP_INPUT_PRE_DROP_COOKIE; import org.openkilda.messaging.info.event.ArpInfoData; +import org.openkilda.messaging.info.event.LacpInfoData; +import org.openkilda.messaging.info.event.LacpPartner; +import org.openkilda.messaging.info.event.LacpState; import org.openkilda.messaging.info.event.LldpInfoData; import org.openkilda.model.Flow; +import org.openkilda.model.MacAddress; import org.openkilda.model.PathId; import org.openkilda.model.Switch; import org.openkilda.model.SwitchConnectedDevice; @@ -32,6 +36,7 @@ import org.openkilda.model.TransitVlan; import org.openkilda.persistence.inmemory.InMemoryGraphBasedTest; import org.openkilda.persistence.repositories.FlowRepository; +import org.openkilda.persistence.repositories.LacpPartnerRepository; import org.openkilda.persistence.repositories.SwitchConnectedDeviceRepository; import org.openkilda.persistence.repositories.SwitchRepository; import org.openkilda.persistence.repositories.TransitVlanRepository; @@ -82,10 +87,15 @@ public class PacketServiceTest extends InMemoryGraphBasedTest { public static final int TTL_1 = 120; public static final int TTL_2 = 240; + public static final int LOGICAL_PORT_NUMBER_123 = 123; + public static final int LOGICAL_PORT_NUMBER_456 = 456; + + private static SwitchConnectedDeviceRepository switchConnectedDeviceRepository; private static SwitchRepository switchRepository; private static FlowRepository flowRepository; private static TransitVlanRepository transitVlanRepository; + private static LacpPartnerRepository lacpPartnerRepository; private static PacketService packetService; @BeforeClass @@ -95,6 +105,7 @@ public static void setUpOnce() { switchRepository = persistenceManager.getRepositoryFactory().createSwitchRepository(); flowRepository = persistenceManager.getRepositoryFactory().createFlowRepository(); transitVlanRepository = persistenceManager.getRepositoryFactory().createTransitVlanRepository(); + lacpPartnerRepository = persistenceManager.getRepositoryFactory().createLacpPartnerRepository(); } @Before @@ -121,6 +132,23 @@ public void testHandleArpDataSameTimeOnCreate() { assertEquals(devices.iterator().next().getTimeFirstSeen(), devices.iterator().next().getTimeLastSeen()); } + @Test + public void testLacpPartnerCreate() { + packetService.handleLacpData(createLacpInfoData()); + Collection devices = lacpPartnerRepository.findAll(); + assertEquals(1, devices.size()); + } + + @Test + public void testLacpPartnerUpdateAndCreate() { + packetService.handleLacpData(createLacpInfoData()); + LacpInfoData lacpInfoData = createLacpInfoData(); + lacpInfoData.setLogicalPortNumber(LOGICAL_PORT_NUMBER_456); + packetService.handleLacpData(lacpInfoData); + Collection devices = lacpPartnerRepository.findAll(); + assertEquals(2, devices.size()); + } + @Test public void testHandleLldpDataDifferentTimeOnUpdate() throws InterruptedException { // create @@ -285,7 +313,7 @@ public void testHandleArpDataDifferentIpAddress() { } private Object[][] getOneSwitchOnePortFlowParameters() { - return new Object[][] { + return new Object[][]{ // inVlan, srcVlan, dstVlan, vlansInPacket, sourceSwitch {VLAN_0, VLAN_0, VLAN_2, newArrayList(VLAN_2), true}, {VLAN_1, VLAN_0, VLAN_2, newArrayList(VLAN_2, VLAN_1), true}, @@ -309,7 +337,7 @@ public void findFlowRelatedDataForOneSwitchOnePortFlowTest( } private Object[][] getOneSwitchFlowParameters() { - return new Object[][] { + return new Object[][]{ // inVlan, srcVlan, dstVlan, vlansInPacket, sourceSwitch {VLAN_0, VLAN_0, VLAN_0, newArrayList(), true}, {VLAN_1, VLAN_0, VLAN_0, newArrayList(VLAN_1), true}, @@ -338,7 +366,7 @@ public void findFlowRelatedDataForOneSwitchFlowTest( } private Object[][] getVlanFlowParameters() { - return new Object[][] { + return new Object[][]{ // inVlan, srcVlan, dstVlan, transitVlan, vlansInPacket, sourceSwitch {VLAN_0, VLAN_0, VLAN_3, VLAN_2, newArrayList(VLAN_2), true}, {VLAN_1, VLAN_0, VLAN_3, VLAN_2, newArrayList(VLAN_2, VLAN_1), true}, @@ -363,7 +391,7 @@ public void findFlowRelatedDataForVlanFlowTest( } private Object[][] getInOutVlanCombinationForVxlanParameters() { - return new Object[][] { + return new Object[][]{ // inVlan, srcVlan, dstVlan, vlansInPacket, sourceSwitch {VLAN_0, VLAN_0, VLAN_0, newArrayList(), true}, {VLAN_0, VLAN_0, VLAN_2, newArrayList(), true}, @@ -508,4 +536,16 @@ private ArpInfoData createArpInfoData() { return new ArpInfoData(SWITCH_ID_1, PORT_NUMBER_1, newArrayList(VLAN_1), ARP_INPUT_PRE_DROP_COOKIE, MAC_ADDRESS_1, IP_ADDRESS_1); } + + private LacpInfoData createLacpInfoData() { + LacpState state = LacpState.builder() + .active(Boolean.TRUE).shortTimeout(Boolean.TRUE).aggregatable(Boolean.TRUE) + .synchronised(Boolean.TRUE).collecting(Boolean.TRUE).distributing(Boolean.TRUE) + .defaulted(Boolean.TRUE).expired(Boolean.FALSE).build(); + LacpPartner partner = LacpPartner.builder().systemPriority(1).systemId(new MacAddress("00:00:00:00:00:01")) + .key(1).portPriority(1).portNumber(11).state(state).build(); + LacpInfoData lacpInfoData = LacpInfoData.builder().switchId(SWITCH_ID_1) + .logicalPortNumber(LOGICAL_PORT_NUMBER_123).actor(partner).build(); + return lacpInfoData; + } } diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/KafkaChannel.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/KafkaChannel.java index af3b3985aa1..83844e38ccf 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/KafkaChannel.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/KafkaChannel.java @@ -144,6 +144,10 @@ public String getNetworkControlResponseTopic() { return formatTopicWithRegion(topics.getNetworkControlResponseRegionTopic()); } + public String getLacpTopic() { + return formatTopicWithRegion(topics.getLacpTopic()); + } + private String formatTopicWithRegion(String topic) { String region = config.getFloodlightRegion(); if (region == null || region.isEmpty()) { diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/service/lacp/LacpService.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/service/lacp/LacpService.java index 8f51ef64306..d1263541604 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/service/lacp/LacpService.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/service/lacp/LacpService.java @@ -22,12 +22,18 @@ import org.openkilda.floodlight.command.CommandContext; import org.openkilda.floodlight.model.OfInput; import org.openkilda.floodlight.service.IService; +import org.openkilda.floodlight.service.kafka.IKafkaProducerService; import org.openkilda.floodlight.service.kafka.KafkaUtilityService; import org.openkilda.floodlight.service.of.IInputTranslator; import org.openkilda.floodlight.service.of.InputService; import org.openkilda.floodlight.shared.packet.Lacp; import org.openkilda.floodlight.shared.packet.Lacp.ActorPartnerInfo; import org.openkilda.floodlight.shared.packet.SlowProtocols; +import org.openkilda.floodlight.utils.CorrelationContext; +import org.openkilda.messaging.info.InfoMessage; +import org.openkilda.messaging.info.event.LacpInfoData; +import org.openkilda.messaging.info.event.LacpPartner; +import org.openkilda.messaging.info.event.LacpState; import org.openkilda.model.SwitchId; import org.openkilda.model.cookie.Cookie; import org.openkilda.model.cookie.CookieBase.CookieType; @@ -57,11 +63,6 @@ public class LacpService implements IService, IInputTranslator { private static final Logger logger = LoggerFactory.getLogger(LacpService.class); - private IOFSwitchService switchService; - private MacAddress systemId; - private int systemPriority; - private int portPriority; - static { try { logger.info("Force loading of {}", Class.forName(SlowProtocols.class.getName())); @@ -70,6 +71,28 @@ public class LacpService implements IService, IInputTranslator { } } + private IOFSwitchService switchService; + private MacAddress systemId; + private int systemPriority; + private int portPriority; + private IKafkaProducerService producerService; + private String topic; + private String region; + + private static OFPort getInPort(OFPacketIn packetIn) { + if (packetIn.getVersion().compareTo(OFVersion.OF_12) < 0) { + return packetIn.getInPort(); + } + + if (packetIn.getMatch().supports(MatchField.IN_PHY_PORT)) { + OFPort inPort = packetIn.getMatch().get(MatchField.IN_PHY_PORT); + if (inPort != null) { + return inPort; + } + } + return packetIn.getMatch().get(MatchField.IN_PORT); + } + @Override public Command makeCommand(CommandContext context, OfInput input) { return new Command(context) { @@ -92,8 +115,8 @@ Lacp deserializeLacp(Ethernet eth, SwitchId switchId, long cookie) { return (Lacp) slowProtocol.getPayload(); } else { if (logger.isTraceEnabled()) { - logger.trace("Got unknown slow protocol packet {} on switch {}", - slowProtocol.getSubtype(), switchId); + logger.trace("Got unknown slow protocol packet {} on switch {}", slowProtocol.getSubtype(), + switchId); } return null; } @@ -119,8 +142,8 @@ private void handlePacketIn(OfInput input) { if (cookie.getType() == CookieType.LACP_REPLY_INPUT) { if (logger.isDebugEnabled()) { - logger.debug("Receive LACP packet from {} OF-xid:{}, cookie: {}", - input.getDpId(), input.getMessage().getXid(), cookie); + logger.debug("Receive LACP packet from {} OF-xid:{}, cookie: {}", input.getDpId(), + input.getMessage().getXid(), cookie); } handleLacp(input, switchId, cookie.getValue(), getInPort((OFPacketIn) input.getMessage())); } @@ -137,6 +160,33 @@ private void handleLacp(OfInput input, SwitchId switchId, long cookie, OFPort in } Lacp lacpReply = modifyLacpRequest(lacpRequest); sendLacpReply(DatapathId.of(switchId.getId()), inPort, lacpReply); + + LacpInfoData lacpInfoData = getLacpInfoData(lacpRequest, switchId, inPort.getPortNumber()); + + InfoMessage message = new InfoMessage(lacpInfoData, System.currentTimeMillis(), CorrelationContext.getId(), + region); + + producerService.sendMessageAndTrackWithZk(topic, switchId.toString(), message); + } + + private LacpInfoData getLacpInfoData(final Lacp lacpRequest, final SwitchId switchId, final int logicalPortNumber) { + + ActorPartnerInfo actor = lacpRequest.getActor(); + Lacp.State state = actor.getState(); + + LacpState actorState = LacpState.builder() + .active(state.isActive()).shortTimeout(state.isShortTimeout()).aggregatable(state.isAggregatable()) + .synchronised(state.isSynchronised()).collecting(state.isCollecting()) + .distributing(state.isDistributing()).defaulted(state.isDefaulted()).expired(state.isExpired()).build(); + + org.openkilda.model.MacAddress macAddressActor = + new org.openkilda.model.MacAddress(actor.getSystemId().toString()); + + LacpPartner lacpActor = LacpPartner.builder().systemPriority(actor.getSystemPriority()) + .systemId(macAddressActor).key(actor.getKey()).portPriority(actor.getPortPriority()) + .portNumber(actor.getPortNumber()).state(actorState).build(); + + return LacpInfoData.builder().switchId(switchId).logicalPortNumber(logicalPortNumber).actor(lacpActor).build(); } @VisibleForTesting @@ -160,18 +210,15 @@ OFPacketOut generateLacpPacket(IOFSwitch sw, Lacp lacp, OFPort outPort) { slowProtocols.setSubtype(SlowProtocols.LACP_SUBTYPE); slowProtocols.setPayload(lacp); - Ethernet l2 = new Ethernet().setSourceMACAddress(new SwitchId(sw.getId().getLong()).toMacAddress()) - .setDestinationMACAddress(org.openkilda.model.MacAddress.SLOW_PROTOCOLS.getAddress()) - .setEtherType(EthType.SLOW_PROTOCOLS); + Ethernet l2 = new Ethernet().setSourceMACAddress( + new SwitchId(sw.getId().getLong()).toMacAddress()).setDestinationMACAddress( + org.openkilda.model.MacAddress.SLOW_PROTOCOLS.getAddress()).setEtherType(EthType.SLOW_PROTOCOLS); l2.setPayload(slowProtocols); byte[] data = l2.serialize(); - OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut() - .setBufferId(OFBufferId.NO_BUFFER) - .setActions( - Lists.newArrayList( - sw.getOFFactory().actions().buildOutput().setPort(outPort).build())) - .setData(data); + OFPacketOut.Builder pob = sw.getOFFactory().buildPacketOut().setBufferId(OFBufferId.NO_BUFFER).setActions( + Lists.newArrayList(sw.getOFFactory().actions().buildOutput().setPort(outPort).build())).setData( + data); OFMessageUtils.setInPort(pob, OFPort.CONTROLLER); return pob.build(); @@ -198,8 +245,8 @@ private void sendLacpReply(DatapathId dpId, OFPort port, Lacp lacpReply) { } if (!result) { - logger.error("Failed to send PACKET_OUT(LACP reply packet) via {}-{}. Packet {}", - sw.getId(), port.getPortNumber(), lacpReply); + logger.error("Failed to send PACKET_OUT(LACP reply packet) via {}-{}. Packet {}", sw.getId(), + port.getPortNumber(), lacpReply); } } else { logger.error("Couldn't find switch {} to send LACP reply", switchId); @@ -209,20 +256,6 @@ private void sendLacpReply(DatapathId dpId, OFPort port, Lacp lacpReply) { } } - private static OFPort getInPort(OFPacketIn packetIn) { - if (packetIn.getVersion().compareTo(OFVersion.OF_12) < 0) { - return packetIn.getInPort(); - } - - if (packetIn.getMatch().supports(MatchField.IN_PHY_PORT)) { - OFPort inPort = packetIn.getMatch().get(MatchField.IN_PHY_PORT); - if (inPort != null) { - return inPort; - } - } - return packetIn.getMatch().get(MatchField.IN_PORT); - } - @Override public void setup(FloodlightModuleContext context) { logger.info("Stating {}", LacpService.class.getCanonicalName()); @@ -238,5 +271,10 @@ public void setup(FloodlightModuleContext context) { InputService inputService = context.getServiceImpl(InputService.class); inputService.addTranslator(OFType.PACKET_IN, this); + + + region = kafkaChannel.getRegion(); + producerService = context.getServiceImpl(IKafkaProducerService.class); + topic = kafkaChannel.getLacpTopic(); } } diff --git a/src-java/floodlightrouter-topology/floodlightrouter-storm-topology/src/main/java/org/openkilda/wfm/topology/floodlightrouter/ComponentType.java b/src-java/floodlightrouter-topology/floodlightrouter-storm-topology/src/main/java/org/openkilda/wfm/topology/floodlightrouter/ComponentType.java index 87fd4a05c9c..c5c8a91eaa4 100644 --- a/src-java/floodlightrouter-topology/floodlightrouter-storm-topology/src/main/java/org/openkilda/wfm/topology/floodlightrouter/ComponentType.java +++ b/src-java/floodlightrouter-topology/floodlightrouter-storm-topology/src/main/java/org/openkilda/wfm/topology/floodlightrouter/ComponentType.java @@ -25,6 +25,7 @@ public final class ComponentType { public static final String KILDA_STATS_REPLY_BOLT = "KILDA_STATS_REPLY_BOLT"; public static final String KILDA_ISL_LATENCY_REPLY_BOLT = "KILDA_ISL_LATENCY_REPLY_BOLT"; public static final String KILDA_CONNECTED_DEVICES_REPLY_BOLT = "KILDA_CONNECTED_DEVICES_REPLY_BOLT"; + public static final String KILDA_LACP_REPLY_BOLT = "KILDA_LACP_REPLY_BOLT"; public static final String KILDA_NB_WORKER_REPLY_BOLT = "KILDA_NB_WORKER_REPLY_BOLT"; public static final String KILDA_TOPO_DISCO_REPLY_BOLT = "KILDA_TOPO_DISCO_REPLY_BOLT"; public static final String KILDA_TOPO_DISCO_BOLT = "KILDA_TOPO_DISCO_BOLT"; @@ -47,6 +48,7 @@ public final class ComponentType { public static final String KILDA_STATS_KAFKA_SPOUT = "KILDA_STATS_KAFKA_SPOUT"; public static final String KILDA_ISL_LATENCY_KAFKA_SPOUT = "KILDA_ISL_LATENCY_KAFKA_SPOUT"; public static final String KILDA_CONNECTED_DEVICES_KAFKA_SPOUT = "KILDA_CONNECTED_DEVICES_KAFKA_SPOUT"; + public static final String KILDA_LACP_KAFKA_SPOUT = "KILDA_LACP_KAFKA_SPOUT"; public static final String KILDA_SWITCH_MANAGER_KAFKA_SPOUT = "KILDA_SWITCH_MANAGER_KAFKA_SPOUT"; public static final String SPEAKER_DISCO_KAFKA_SPOUT = "SPEAKER_DISCO_KAFKA_SPOUT"; public static final String KILDA_TOPO_DISCO_KAFKA_SPOUT = "KILDA_TOPO_DISCO_KAFKA_SPOUT"; diff --git a/src-java/floodlightrouter-topology/floodlightrouter-storm-topology/src/main/java/org/openkilda/wfm/topology/floodlightrouter/FloodlightRouterTopology.java b/src-java/floodlightrouter-topology/floodlightrouter-storm-topology/src/main/java/org/openkilda/wfm/topology/floodlightrouter/FloodlightRouterTopology.java index 9aec333ec20..16cd23f2531 100644 --- a/src-java/floodlightrouter-topology/floodlightrouter-storm-topology/src/main/java/org/openkilda/wfm/topology/floodlightrouter/FloodlightRouterTopology.java +++ b/src-java/floodlightrouter-topology/floodlightrouter-storm-topology/src/main/java/org/openkilda/wfm/topology/floodlightrouter/FloodlightRouterTopology.java @@ -229,6 +229,12 @@ private void speakerToConnectedDevices(TopologyBuilder topology, TopologyOutput topology, kafkaTopics.getTopoConnectedDevicesRegionTopic(), kafkaTopics.getTopoConnectedDevicesTopic(), ComponentType.KILDA_CONNECTED_DEVICES_KAFKA_SPOUT, ComponentType.KILDA_CONNECTED_DEVICES_REPLY_BOLT, output.getKafkaGenericOutput()); + + declareSpeakerToControllerProxy( + topology, kafkaTopics.getLacpRegionTopic(), kafkaTopics.getLacpTopic(), + ComponentType.KILDA_LACP_KAFKA_SPOUT, ComponentType.KILDA_LACP_REPLY_BOLT, + output.getKafkaGenericOutput()); + } private void speakerToSwitchManager(TopologyBuilder topology, TopologyOutput output) { diff --git a/src-java/kilda-configuration/src/main/java/org/openkilda/config/KafkaTopicsConfig.java b/src-java/kilda-configuration/src/main/java/org/openkilda/config/KafkaTopicsConfig.java index 0a64d20b952..d92b8d01bd2 100644 --- a/src-java/kilda-configuration/src/main/java/org/openkilda/config/KafkaTopicsConfig.java +++ b/src-java/kilda-configuration/src/main/java/org/openkilda/config/KafkaTopicsConfig.java @@ -263,4 +263,12 @@ public interface KafkaTopicsConfig { @Key("stats.notify.priv") @Default("kilda.stats.notify.priv") String getFlowStatsNotifyTopic(); + + @Key("topo.lacp.storm") + @Default("kilda.lacp.storm.priv") + String getLacpTopic(); + + @Key("topo.floodlight.lacp.region") + @Default("kilda.floodlight.lacp.priv") + String getLacpRegionTopic(); } diff --git a/src-java/kilda-model/src/main/java/org/openkilda/model/LacpPartner.java b/src-java/kilda-model/src/main/java/org/openkilda/model/LacpPartner.java new file mode 100644 index 00000000000..ac83a8e0d65 --- /dev/null +++ b/src-java/kilda-model/src/main/java/org/openkilda/model/LacpPartner.java @@ -0,0 +1,256 @@ +/* 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.model; + +import com.esotericsoftware.kryo.DefaultSerializer; +import com.esotericsoftware.kryo.serializers.BeanSerializer; +import com.fasterxml.jackson.annotation.JsonIgnore; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.NonNull; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Delegate; +import org.apache.commons.lang3.builder.EqualsBuilder; +import org.mapstruct.Mapper; +import org.mapstruct.MappingTarget; +import org.mapstruct.factory.Mappers; + +import java.io.Serializable; +import java.util.Objects; + +/** + * Represents a physical port. + */ +@DefaultSerializer(BeanSerializer.class) +@ToString +public class LacpPartner implements CompositeDataEntity { + @Getter + @Setter + @Delegate + @JsonIgnore + private LacpPartnerData data; + + /** + * No args constructor for deserialization purpose. + */ + private LacpPartner() { + data = new LacpPartnerDataImpl(); + } + + @Builder + public LacpPartner(@NonNull SwitchId switchId, int logicalPortNumber, int systemPriority, + MacAddress systemId, int key, int portPriority, int portNumber, boolean stateActive, + boolean stateShortTimeout, boolean stateAggregatable, boolean stateSynchronised, + boolean stateCollecting, boolean stateDistributing, boolean stateDefaulted, + boolean stateExpired) { + data = LacpPartnerDataImpl.builder() + .switchId(switchId) + .logicalPortNumber(logicalPortNumber) + .systemPriority(systemPriority) + .systemId(systemId) + .key(key) + .portPriority(portPriority) + .portNumber(portNumber) + .stateActive(stateActive) + .stateShortTimeout(stateShortTimeout) + .stateAggregatable(stateAggregatable) + .stateSynchronised(stateSynchronised) + .stateCollecting(stateCollecting) + .stateDistributing(stateDistributing) + .stateDefaulted(stateDefaulted) + .stateExpired(stateExpired) + .build(); + } + + public LacpPartner(@NonNull LacpPartner.LacpPartnerData data) { + this.data = data; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + LacpPartner that = (LacpPartner) o; + return new EqualsBuilder() + .append(getSwitchId(), that.getSwitchId()) + .append(getLogicalPortNumber(), that.getLogicalPortNumber()) + .append(getSystemPriority(), that.getSystemPriority()) + .append(getSystemId(), that.getSystemId()) + .append(getKey(), that.getKey()) + .append(getPortPriority(), that.getPortPriority()) + .append(getPortNumber(), that.getPortNumber()) + .append(isStateActive(), that.isStateActive()) + .append(isStateShortTimeout(), that.isStateShortTimeout()) + .append(isStateAggregatable(), that.isStateAggregatable()) + .append(isStateSynchronised(), that.isStateSynchronised()) + .append(isStateCollecting(), that.isStateCollecting()) + .append(isStateDistributing(), that.isStateDistributing()) + .append(isStateDefaulted(), that.isStateDefaulted()) + .append(isStateExpired(), that.isStateExpired()) + .isEquals(); + } + + @Override + public int hashCode() { + return Objects.hash(getSwitchId(), getLogicalPortNumber(), getSystemPriority(), getSystemId(), + getKey(), getPortPriority(), getPortNumber(), isStateActive(), isStateShortTimeout(), + isStateAggregatable(), isStateSynchronised(), isStateCollecting(), isStateDistributing(), + isStateDefaulted(), isStateExpired()); + } + + /** + * Defines persistable data of the PhysicalPort. + */ + public interface LacpPartnerData { + SwitchId getSwitchId(); + + void setSwitchId(SwitchId switchId); + + public int getLogicalPortNumber(); + + public void setLogicalPortNumber(int logicalPortNumber); + + int getSystemPriority(); + + void setSystemPriority(int systemPriority); + + MacAddress getSystemId(); + + void setSystemId(MacAddress systemId); + + int getKey(); + + void setKey(int key); + + int getPortPriority(); + + void setPortPriority(int portPriority); + + int getPortNumber(); + + void setPortNumber(int portNumber); + + boolean isStateActive(); + + void setStateActive(boolean stateActive); + + boolean isStateShortTimeout(); + + void setStateShortTimeout(boolean stateShortTimeout); + + boolean isStateAggregatable(); + + void setStateAggregatable(boolean stateAggregatable); + + boolean isStateSynchronised(); + + void setStateSynchronised(boolean stateSynchronised); + + boolean isStateCollecting(); + + void setStateCollecting(boolean stateCollecting); + + boolean isStateDistributing(); + + void setStateDistributing(boolean stateDistributing); + + boolean isStateDefaulted(); + + void setStateDefaulted(boolean stateDefaulted); + + boolean isStateExpired(); + + void setStateExpired(boolean stateExpired); + } + + /** + * A cloner for LacpPartner entity. + */ + @Mapper + public interface LacpPartnerCloner { + LacpPartner.LacpPartnerCloner INSTANCE = Mappers.getMapper(LacpPartner.LacpPartnerCloner.class); + + /** + * Performs copy of entity data. + */ + default void copy(LacpPartnerData source, @MappingTarget LacpPartnerData target) { + if (source == null) { + return; + } + target.setSwitchId(source.getSwitchId()); + target.setLogicalPortNumber(source.getLogicalPortNumber()); + target.setSystemPriority(source.getSystemPriority()); + target.setSystemId(source.getSystemId()); + target.setKey(source.getKey()); + target.setPortPriority(source.getPortPriority()); + target.setPortNumber(source.getPortNumber()); + target.setStateActive(source.isStateActive()); + target.setStateShortTimeout(source.isStateShortTimeout()); + target.setStateAggregatable(source.isStateAggregatable()); + target.setStateSynchronised(source.isStateSynchronised()); + target.setStateCollecting(source.isStateCollecting()); + target.setStateDistributing(source.isStateDistributing()); + target.setStateDefaulted(source.isStateDefaulted()); + target.setStateExpired(source.isStateExpired()); + } + + /** + * Performs deep copy of entity data. + */ + default LacpPartnerData deepCopy(LacpPartnerData source) { + LacpPartnerData result = new LacpPartnerDataImpl(); + copy(source, result); + return result; + } + } + + /** + * POJO implementation of PhysicalPortData. + */ + + @Data + @Builder + @NoArgsConstructor + @AllArgsConstructor + static final class LacpPartnerDataImpl implements LacpPartnerData, Serializable { + private static final long serialVersionUID = 1L; + @NonNull SwitchId switchId; + + int logicalPortNumber; + + int systemPriority; + MacAddress systemId; + int key; + int portPriority; + int portNumber; + boolean stateActive; + boolean stateShortTimeout; + boolean stateAggregatable; + boolean stateSynchronised; + boolean stateCollecting; + boolean stateDistributing; + boolean stateDefaulted; + boolean stateExpired; + } +} diff --git a/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/LacpPartnerRepository.java b/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/LacpPartnerRepository.java new file mode 100644 index 00000000000..58e09e6fa4a --- /dev/null +++ b/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/LacpPartnerRepository.java @@ -0,0 +1,30 @@ +/* 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.persistence.repositories; + +import org.openkilda.model.LacpPartner; +import org.openkilda.model.SwitchId; + +import java.util.Collection; +import java.util.Optional; + +public interface LacpPartnerRepository extends Repository { + Collection findAll(); + + Collection findBySwitchId(SwitchId switchId); + + Optional findBySwitchIdAndLogicalPortNumber(SwitchId switchId, int logicalPortNumber); +} diff --git a/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryAreaBinding.java b/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryAreaBinding.java index 27d6da6d004..44f63629214 100644 --- a/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryAreaBinding.java +++ b/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryAreaBinding.java @@ -60,6 +60,7 @@ private RepositoryAreaBinding() { binding.put(FlowStatsRepository.class, PersistenceArea.COMMON); binding.put(YFlowRepository.class, PersistenceArea.COMMON); binding.put(PortRepository.class, PersistenceArea.COMMON); + binding.put(LacpPartnerRepository.class, PersistenceArea.COMMON); binding.put(HaFlowRepository.class, PersistenceArea.COMMON); binding.put(HaSubFlowRepository.class, PersistenceArea.COMMON); binding.put(HaFlowPathRepository.class, PersistenceArea.COMMON); @@ -83,12 +84,12 @@ public PersistenceArea lookup(Class klass) { queue.addFirst(klass); PersistenceArea result = null; - while (! queue.isEmpty()) { + while (!queue.isEmpty()) { Class entry = queue.pollFirst(); if (entry == null) { continue; } - if (! Repository.class.isAssignableFrom(entry)) { + if (!Repository.class.isAssignableFrom(entry)) { continue; } @@ -107,3 +108,4 @@ public PersistenceArea lookup(Class klass) { return result; } } + diff --git a/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryFactory.java b/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryFactory.java index beb90749ae2..b05c850b0a3 100644 --- a/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryFactory.java +++ b/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryFactory.java @@ -88,9 +88,13 @@ public interface RepositoryFactory { PortRepository createPortRepository(); + LacpPartnerRepository createLacpPartnerRepository(); + HaFlowRepository createHaFlowRepository(); HaSubFlowRepository createHaSubFlowRepository(); HaFlowPathRepository createHaFlowPathRepository(); } + + diff --git a/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryFactoryProxy.java b/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryFactoryProxy.java index 849b7c308d9..3f008f6323b 100644 --- a/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryFactoryProxy.java +++ b/src-java/kilda-persistence-api/src/main/java/org/openkilda/persistence/repositories/RepositoryFactoryProxy.java @@ -190,6 +190,11 @@ public PortRepository createPortRepository() { return resolve(PortRepository.class).createPortRepository(); } + @Override + public LacpPartnerRepository createLacpPartnerRepository() { + return resolve(LacpPartnerRepository.class).createLacpPartnerRepository(); + } + @Override public HaFlowRepository createHaFlowRepository() { return resolve(HaFlowRepository.class).createHaFlowRepository(); @@ -211,3 +216,4 @@ private RepositoryFactory resolve(Class repositoryClass) { return implementation.getRepositoryFactory(); } } + diff --git a/src-java/kilda-persistence-hibernate/src/main/java/org/openkilda/persistence/hibernate/HibernateRepositoryFactory.java b/src-java/kilda-persistence-hibernate/src/main/java/org/openkilda/persistence/hibernate/HibernateRepositoryFactory.java index b8b16b030d3..f894b77990b 100644 --- a/src-java/kilda-persistence-hibernate/src/main/java/org/openkilda/persistence/hibernate/HibernateRepositoryFactory.java +++ b/src-java/kilda-persistence-hibernate/src/main/java/org/openkilda/persistence/hibernate/HibernateRepositoryFactory.java @@ -35,6 +35,7 @@ import org.openkilda.persistence.repositories.IslRepository; import org.openkilda.persistence.repositories.KildaConfigurationRepository; import org.openkilda.persistence.repositories.KildaFeatureTogglesRepository; +import org.openkilda.persistence.repositories.LacpPartnerRepository; import org.openkilda.persistence.repositories.LagLogicalPortRepository; import org.openkilda.persistence.repositories.LinkPropsRepository; import org.openkilda.persistence.repositories.MirrorGroupRepository; @@ -213,6 +214,11 @@ public PortRepository createPortRepository() { throw new IllegalStateException("Repository not implemented on hibernate layer"); } + @Override + public LacpPartnerRepository createLacpPartnerRepository() { + throw new IllegalStateException("The Repository is not implemented on hibernate layer"); + } + @Override public LagLogicalPortRepository createLagLogicalPortRepository() { throw new IllegalStateException("Repository not implemented on hibernate layer"); diff --git a/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/frames/LacpPartnerFrame.java b/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/frames/LacpPartnerFrame.java new file mode 100644 index 00000000000..ca5ac4f547b --- /dev/null +++ b/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/frames/LacpPartnerFrame.java @@ -0,0 +1,185 @@ +/* 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.persistence.ferma.frames; + + +import org.openkilda.model.LacpPartner.LacpPartnerData; +import org.openkilda.model.MacAddress; +import org.openkilda.model.SwitchId; +import org.openkilda.persistence.ferma.frames.converters.Convert; +import org.openkilda.persistence.ferma.frames.converters.MacAddressConverter; +import org.openkilda.persistence.ferma.frames.converters.SwitchIdConverter; + +import com.syncleus.ferma.FramedGraph; +import com.syncleus.ferma.annotations.Property; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; + +import java.util.List; +import java.util.Optional; + +@Slf4j +public abstract class LacpPartnerFrame extends KildaBaseVertexFrame implements LacpPartnerData { + public static final String FRAME_LABEL = "lacp_partner"; + public static final String SWITCH_ID_PROPERTY = "switch_id"; + public static final String LOGICAL_PORT_NUMBER_PROPERTY = "logical_port_number"; + public static final String SYSTEM_PRIORITY_PROPERTY = "system_priority"; + public static final String SYSTEM_ID_PROPERTY = "system_id"; + public static final String KEY_PROPERTY = "key"; + public static final String PORT_PRIORITY_PROPERTY = "port_priority"; + public static final String PORT_NUMBER_PROPERTY = "port_number"; + public static final String STATE_ACTIVE_PROPERTY = "state_active"; + public static final String STATE_SHORT_TIMEOUT_PROPERTY = "state_short_timeout"; + public static final String STATE_AGGREGATABLE_PROPERTY = "state_aggregatable"; + public static final String STATE_SYNCHRONISED_PROPERTY = "state_synchronised"; + public static final String STATE_COLLECTING_PROPERTY = "state_collecting"; + public static final String STATE_DISTRIBUTING_PROPERTY = "state_distributing"; + public static final String STATE_DEFAULTED_PROPERTY = "state_defaulted"; + public static final String STATE_EXPIRED_PROPERTY = "state_expired"; + + public static Optional load(FramedGraph graph, String switchId, int logicalPortNumber) { + List lacpPartnerFrames = graph.traverse(input -> input.V() + .hasLabel(FRAME_LABEL) + .has(SWITCH_ID_PROPERTY, switchId) + .has(LOGICAL_PORT_NUMBER_PROPERTY, logicalPortNumber)) + .toListExplicit(LacpPartnerFrame.class); + return lacpPartnerFrames.isEmpty() ? Optional.empty() : Optional.of(lacpPartnerFrames.get(0)); + } + + @Override + @Property(SWITCH_ID_PROPERTY) + @Convert(SwitchIdConverter.class) + public abstract SwitchId getSwitchId(); + + @Override + @Property(SWITCH_ID_PROPERTY) + @Convert(SwitchIdConverter.class) + public abstract void setSwitchId(@NonNull SwitchId switchId); + + @Override + @Property(LOGICAL_PORT_NUMBER_PROPERTY) + public abstract int getLogicalPortNumber(); + + @Override + @Property(LOGICAL_PORT_NUMBER_PROPERTY) + public abstract void setLogicalPortNumber(int logicalPortNumber); + + @Override + @Property(SYSTEM_PRIORITY_PROPERTY) + public abstract int getSystemPriority(); + + @Override + @Property(SYSTEM_PRIORITY_PROPERTY) + public abstract void setSystemPriority(int systemPriority); + + @Override + @Property(SYSTEM_ID_PROPERTY) + @Convert(MacAddressConverter.class) + public abstract MacAddress getSystemId(); + + @Override + @Property(SYSTEM_ID_PROPERTY) + @Convert(MacAddressConverter.class) + public abstract void setSystemId(MacAddress systemId); + + @Override + @Property(KEY_PROPERTY) + public abstract int getKey(); + + @Override + @Property(KEY_PROPERTY) + public abstract void setKey(int key); + + @Override + @Property(PORT_PRIORITY_PROPERTY) + public abstract int getPortPriority(); + + @Override + @Property(PORT_PRIORITY_PROPERTY) + public abstract void setPortPriority(int portPriority); + + @Override + @Property(PORT_NUMBER_PROPERTY) + public abstract int getPortNumber(); + + @Override + @Property(PORT_NUMBER_PROPERTY) + public abstract void setPortNumber(int portNumber); + + @Override + @Property(STATE_ACTIVE_PROPERTY) + public abstract boolean isStateActive(); + + @Override + @Property(STATE_ACTIVE_PROPERTY) + public abstract void setStateActive(boolean stateActive); + + @Override + @Property(STATE_SHORT_TIMEOUT_PROPERTY) + public abstract boolean isStateShortTimeout(); + + @Override + @Property(STATE_SHORT_TIMEOUT_PROPERTY) + public abstract void setStateShortTimeout(boolean stateShortTimeout); + + @Override + @Property(STATE_AGGREGATABLE_PROPERTY) + public abstract boolean isStateAggregatable(); + + @Override + @Property(STATE_AGGREGATABLE_PROPERTY) + public abstract void setStateAggregatable(boolean stateAggregatable); + + @Override + @Property(STATE_SYNCHRONISED_PROPERTY) + public abstract boolean isStateSynchronised(); + + @Override + @Property(STATE_SYNCHRONISED_PROPERTY) + public abstract void setStateSynchronised(boolean stateSynchronised); + + @Override + @Property(STATE_COLLECTING_PROPERTY) + public abstract boolean isStateCollecting(); + + @Override + @Property(STATE_COLLECTING_PROPERTY) + public abstract void setStateCollecting(boolean stateCollecting); + + @Override + @Property(STATE_DISTRIBUTING_PROPERTY) + public abstract boolean isStateDistributing(); + + @Override + @Property(STATE_DISTRIBUTING_PROPERTY) + public abstract void setStateDistributing(boolean stateDistributing); + + @Override + @Property(STATE_DEFAULTED_PROPERTY) + public abstract boolean isStateDefaulted(); + + @Override + @Property(STATE_DEFAULTED_PROPERTY) + public abstract void setStateDefaulted(boolean stateDefaulted); + + @Override + @Property(STATE_EXPIRED_PROPERTY) + public abstract boolean isStateExpired(); + + @Override + @Property(STATE_EXPIRED_PROPERTY) + public abstract void setStateExpired(boolean stateExpired); +} diff --git a/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/repositories/FermaLacpPartnerRepository.java b/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/repositories/FermaLacpPartnerRepository.java new file mode 100644 index 00000000000..55ff244baba --- /dev/null +++ b/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/repositories/FermaLacpPartnerRepository.java @@ -0,0 +1,98 @@ +/* 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.persistence.ferma.repositories; + +import org.openkilda.model.LacpPartner; +import org.openkilda.model.LacpPartner.LacpPartnerData; +import org.openkilda.model.SwitchId; +import org.openkilda.persistence.ferma.FermaPersistentImplementation; +import org.openkilda.persistence.ferma.frames.KildaBaseVertexFrame; +import org.openkilda.persistence.ferma.frames.LacpPartnerFrame; +import org.openkilda.persistence.ferma.frames.converters.SwitchIdConverter; +import org.openkilda.persistence.repositories.LacpPartnerRepository; +import org.openkilda.persistence.repositories.PortRepository; + +import lombok.extern.slf4j.Slf4j; + +import java.util.Collection; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Ferma implementation of {@link PortRepository}. + */ +@Slf4j +public class FermaLacpPartnerRepository extends FermaGenericRepository + implements LacpPartnerRepository { + public FermaLacpPartnerRepository(FermaPersistentImplementation implementation) { + super(implementation); + } + + @Override + protected LacpPartnerFrame doAdd(LacpPartnerData data) { + LacpPartnerFrame frame = KildaBaseVertexFrame.addNewFramedVertex(framedGraph(), + LacpPartnerFrame.FRAME_LABEL, LacpPartnerFrame.class); + + LacpPartner.LacpPartnerCloner.INSTANCE.copy(data, frame); + return frame; + } + + @Override + protected void doRemove(LacpPartnerFrame frame) { + frame.remove(); + } + + @Override + protected LacpPartnerData doDetach(LacpPartner entity, LacpPartnerFrame frame) { + return LacpPartner.LacpPartnerCloner.INSTANCE.deepCopy(frame); + } + + @Override + public Collection findAll() { + return framedGraph().traverse(g -> g.V() + .hasLabel(LacpPartnerFrame.FRAME_LABEL)) + .toListExplicit(LacpPartnerFrame.class).stream() + .map(LacpPartner::new) + .collect(Collectors.toList()); + } + + @Override + public Collection findBySwitchId(SwitchId switchId) { + return framedGraph().traverse(g -> g.V() + .hasLabel(LacpPartnerFrame.FRAME_LABEL) + .has(LacpPartnerFrame.SWITCH_ID_PROPERTY, SwitchIdConverter.INSTANCE.toGraphProperty(switchId))) + .toListExplicit(LacpPartnerFrame.class).stream() + .map(LacpPartner::new) + .collect(Collectors.toList()); + } + + @Override + public Optional findBySwitchIdAndLogicalPortNumber(SwitchId switchId, int logicalPortNumber) { + List lacpPartnerFrames = framedGraph().traverse(g -> g.V() + .hasLabel(LacpPartnerFrame.FRAME_LABEL) + .has(LacpPartnerFrame.SWITCH_ID_PROPERTY, SwitchIdConverter.INSTANCE.toGraphProperty(switchId)) + .has(LacpPartnerFrame.LOGICAL_PORT_NUMBER_PROPERTY, logicalPortNumber)) + .toListExplicit(LacpPartnerFrame.class); + if (lacpPartnerFrames.size() > 1) { + log.error(String.format("More then one LACP status fields for switch %s on port %s", + switchId, logicalPortNumber)); + } + + return lacpPartnerFrames.isEmpty() ? Optional.empty() : Optional.of(lacpPartnerFrames.get(0)) + .map(LacpPartner::new); + } +} diff --git a/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/repositories/FermaRepositoryFactory.java b/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/repositories/FermaRepositoryFactory.java index ccf1963a563..71ee35f79e2 100644 --- a/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/repositories/FermaRepositoryFactory.java +++ b/src-java/kilda-persistence-tinkerpop/src/main/java/org/openkilda/persistence/ferma/repositories/FermaRepositoryFactory.java @@ -33,6 +33,7 @@ import org.openkilda.persistence.repositories.IslRepository; import org.openkilda.persistence.repositories.KildaConfigurationRepository; import org.openkilda.persistence.repositories.KildaFeatureTogglesRepository; +import org.openkilda.persistence.repositories.LacpPartnerRepository; import org.openkilda.persistence.repositories.LagLogicalPortRepository; import org.openkilda.persistence.repositories.LinkPropsRepository; import org.openkilda.persistence.repositories.MirrorGroupRepository; @@ -230,6 +231,11 @@ public PortRepository createPortRepository() { return new FermaPortRepository(implementation); } + @Override + public LacpPartnerRepository createLacpPartnerRepository() { + return new FermaLacpPartnerRepository(implementation); + } + @Override public YFlowRepository createYFlowRepository() { return new FermaYFlowRepository(implementation); diff --git a/src-java/kilda-persistence-tinkerpop/src/test/java/org/openkilda/persistence/ferma/repositories/FermaLacpPartnerRepositoryTest.java b/src-java/kilda-persistence-tinkerpop/src/test/java/org/openkilda/persistence/ferma/repositories/FermaLacpPartnerRepositoryTest.java new file mode 100644 index 00000000000..bb5141e53f9 --- /dev/null +++ b/src-java/kilda-persistence-tinkerpop/src/test/java/org/openkilda/persistence/ferma/repositories/FermaLacpPartnerRepositoryTest.java @@ -0,0 +1,117 @@ +/* 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.persistence.ferma.repositories; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; + +import org.openkilda.model.LacpPartner; +import org.openkilda.model.MacAddress; +import org.openkilda.model.SwitchId; +import org.openkilda.persistence.inmemory.InMemoryGraphBasedTest; +import org.openkilda.persistence.repositories.LacpPartnerRepository; + +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Collection; +import java.util.Optional; + +public class FermaLacpPartnerRepositoryTest extends InMemoryGraphBasedTest { + public static final SwitchId TEST_SWITCH_ID_1 = new SwitchId(1); + public static final SwitchId TEST_SWITCH_ID_2 = new SwitchId(2); + public static final int LOGICAL_PORT_NUMBER_1 = 1; + public static final int LOGICAL_PORT_NUMBER_2 = 2; + + private static LacpPartnerRepository lacpPartnerRepository; + + @BeforeClass + public static void setUp() { + lacpPartnerRepository = repositoryFactory.createLacpPartnerRepository(); + } + + @Before + public void init() { + lacpPartnerRepository.add(buildLacpPartner(TEST_SWITCH_ID_1, LOGICAL_PORT_NUMBER_1)); + lacpPartnerRepository.add(buildLacpPartner(TEST_SWITCH_ID_1, LOGICAL_PORT_NUMBER_2)); + lacpPartnerRepository.add(buildLacpPartner(TEST_SWITCH_ID_2, LOGICAL_PORT_NUMBER_1)); + } + + @Test + public void fildAllReturnsAllLacpPartnersSuccess() { + Collection lacpPartners = lacpPartnerRepository.findAll(); + assertEquals(3, lacpPartners.size()); + } + + @Test + public void fildBySwitchIdReturnsSwitchLacpPartnersSuccess() { + Collection lacpPartnersBySwitch1 = lacpPartnerRepository.findBySwitchId(TEST_SWITCH_ID_1); + assertEquals(2, lacpPartnersBySwitch1.size()); + + Collection lacpPartnersBySwitch2 = lacpPartnerRepository.findBySwitchId(TEST_SWITCH_ID_2); + assertEquals(1, lacpPartnersBySwitch2.size()); + } + + @Test + public void fildBySwitchIdAndLogicalPortNumberReturnsLacpPartnersSuccess() { + Optional lacpPartner = lacpPartnerRepository.findBySwitchIdAndLogicalPortNumber(TEST_SWITCH_ID_1, + LOGICAL_PORT_NUMBER_1); + assertTrue(lacpPartner.isPresent()); + + lacpPartner = lacpPartnerRepository.findBySwitchIdAndLogicalPortNumber(TEST_SWITCH_ID_1, LOGICAL_PORT_NUMBER_2); + assertTrue(lacpPartner.isPresent()); + + lacpPartner = lacpPartnerRepository.findBySwitchIdAndLogicalPortNumber(TEST_SWITCH_ID_2, LOGICAL_PORT_NUMBER_1); + assertTrue(lacpPartner.isPresent()); + } + + @Test + public void deleteLacpPartner() { + Optional lacpPartner = lacpPartnerRepository.findBySwitchIdAndLogicalPortNumber(TEST_SWITCH_ID_2, + LOGICAL_PORT_NUMBER_1); + assertTrue(lacpPartner.isPresent()); + + Optional finalLacpPartner = lacpPartner; + transactionManager.doInTransaction(() -> lacpPartnerRepository.remove(finalLacpPartner.get())); + + assertEquals(2, lacpPartnerRepository.findAll().size()); + + lacpPartner = lacpPartnerRepository.findBySwitchIdAndLogicalPortNumber(TEST_SWITCH_ID_2, LOGICAL_PORT_NUMBER_1); + assertFalse(lacpPartner.isPresent()); + } + + private LacpPartner buildLacpPartner(SwitchId switchId, int logicalPortNumber) { + return LacpPartner.builder() + .switchId(switchId) + .logicalPortNumber(logicalPortNumber) + .systemPriority(1) + .systemId(new MacAddress("00:00:00:00:00:01")) + .key(1) + .portPriority(123) + .portNumber(11) + .stateActive(Boolean.TRUE) + .stateShortTimeout(Boolean.TRUE) + .stateAggregatable(Boolean.TRUE) + .stateSynchronised(Boolean.TRUE) + .stateCollecting(Boolean.TRUE) + .stateDistributing(Boolean.TRUE) + .stateDefaulted(Boolean.TRUE) + .stateExpired(Boolean.FALSE) + .build(); + } +} diff --git a/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/request/GetLacpStatusRequest.java b/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/request/GetLacpStatusRequest.java new file mode 100644 index 00000000000..285f988936a --- /dev/null +++ b/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/request/GetLacpStatusRequest.java @@ -0,0 +1,31 @@ +/* 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.messaging.nbtopology.request; + +import org.openkilda.model.SwitchId; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.EqualsAndHashCode; +import lombok.Value; + +@Value +@EqualsAndHashCode(callSuper = false) +@JsonNaming(value = SnakeCaseStrategy.class) +public class GetLacpStatusRequest extends SwitchesBaseRequest { + SwitchId switchId; + int logicalPortNumber; +} diff --git a/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/request/GetSwitchLacpStatusRequest.java b/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/request/GetSwitchLacpStatusRequest.java new file mode 100644 index 00000000000..d3091a85263 --- /dev/null +++ b/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/request/GetSwitchLacpStatusRequest.java @@ -0,0 +1,30 @@ +/* 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.messaging.nbtopology.request; + +import org.openkilda.model.SwitchId; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.EqualsAndHashCode; +import lombok.Value; + +@Value +@EqualsAndHashCode(callSuper = false) +@JsonNaming(value = SnakeCaseStrategy.class) +public class GetSwitchLacpStatusRequest extends SwitchesBaseRequest { + SwitchId switchId; +} diff --git a/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/response/LacpStatusDto.java b/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/response/LacpStatusDto.java new file mode 100644 index 00000000000..622a1fd68ee --- /dev/null +++ b/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/response/LacpStatusDto.java @@ -0,0 +1,46 @@ +/* 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.messaging.nbtopology.response; + +import org.openkilda.model.MacAddress; +import org.openkilda.model.SwitchId; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.Value; + +import java.io.Serializable; + +@Value +@JsonNaming(value = SnakeCaseStrategy.class) +public class LacpStatusDto implements Serializable { + private static final long serialVersionUID = 1L; + SwitchId switchId; + int logicalPortNumber; + int systemPriority; + MacAddress systemId; + int key; + int portPriority; + int portNumber; + boolean stateActive; + boolean stateShortTimeout; + boolean stateAggregatable; + boolean stateSynchronised; + boolean stateCollecting; + boolean stateDistributing; + boolean stateDefaulted; + boolean stateExpired; +} diff --git a/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/response/SwitchLacpStatusResponse.java b/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/response/SwitchLacpStatusResponse.java new file mode 100644 index 00000000000..d61efd03d5f --- /dev/null +++ b/src-java/nbworker-topology/nbworker-messaging/src/main/java/org/openkilda/messaging/nbtopology/response/SwitchLacpStatusResponse.java @@ -0,0 +1,34 @@ +/* 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.messaging.nbtopology.response; + +import org.openkilda.messaging.info.InfoData; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy.SnakeCaseStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.EqualsAndHashCode; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@EqualsAndHashCode(callSuper = false) +@JsonNaming(value = SnakeCaseStrategy.class) +public class SwitchLacpStatusResponse extends InfoData { + private LacpStatusDto data; +} diff --git a/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/share/mappers/LacpStatusMapper.java b/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/share/mappers/LacpStatusMapper.java new file mode 100644 index 00000000000..ab1de7a3b29 --- /dev/null +++ b/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/share/mappers/LacpStatusMapper.java @@ -0,0 +1,30 @@ +/* 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.wfm.share.mappers; + +import org.openkilda.messaging.nbtopology.response.LacpStatusDto; +import org.openkilda.model.LacpPartner; + +import org.mapstruct.Mapper; +import org.mapstruct.factory.Mappers; + +@Mapper +public abstract class LacpStatusMapper { + + public static final LacpStatusMapper INSTANCE = Mappers.getMapper(LacpStatusMapper.class); + + public abstract LacpStatusDto map(LacpPartner partner); +} diff --git a/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/topology/nbworker/bolts/SwitchOperationsBolt.java b/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/topology/nbworker/bolts/SwitchOperationsBolt.java index 4a1b861007c..2c6435b2ccb 100644 --- a/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/topology/nbworker/bolts/SwitchOperationsBolt.java +++ b/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/topology/nbworker/bolts/SwitchOperationsBolt.java @@ -31,8 +31,10 @@ import org.openkilda.messaging.nbtopology.request.DeleteSwitchRequest; import org.openkilda.messaging.nbtopology.request.GetAllSwitchPropertiesRequest; import org.openkilda.messaging.nbtopology.request.GetFlowsPerPortForSwitchRequest; +import org.openkilda.messaging.nbtopology.request.GetLacpStatusRequest; import org.openkilda.messaging.nbtopology.request.GetPortPropertiesRequest; import org.openkilda.messaging.nbtopology.request.GetSwitchConnectedDevicesRequest; +import org.openkilda.messaging.nbtopology.request.GetSwitchLacpStatusRequest; import org.openkilda.messaging.nbtopology.request.GetSwitchLagPortsRequest; import org.openkilda.messaging.nbtopology.request.GetSwitchPropertiesRequest; import org.openkilda.messaging.nbtopology.request.GetSwitchRequest; @@ -47,6 +49,7 @@ import org.openkilda.messaging.nbtopology.response.SwitchConnectedDeviceDto; import org.openkilda.messaging.nbtopology.response.SwitchConnectedDevicesResponse; import org.openkilda.messaging.nbtopology.response.SwitchConnectionsResponse; +import org.openkilda.messaging.nbtopology.response.SwitchLacpStatusResponse; import org.openkilda.messaging.nbtopology.response.SwitchLagPortResponse; import org.openkilda.messaging.nbtopology.response.SwitchPortConnectedDevicesDto; import org.openkilda.messaging.nbtopology.response.SwitchPropertiesResponse; @@ -69,6 +72,7 @@ import org.openkilda.wfm.error.SwitchNotFoundException; import org.openkilda.wfm.error.SwitchPropertiesNotFoundException; import org.openkilda.wfm.share.mappers.ConnectedDeviceMapper; +import org.openkilda.wfm.share.mappers.LacpStatusMapper; import org.openkilda.wfm.share.mappers.LagPortMapper; import org.openkilda.wfm.share.mappers.PortMapper; import org.openkilda.wfm.share.metrics.TimedExecution; @@ -94,6 +98,7 @@ import java.util.List; import java.util.Map; import java.util.Map.Entry; +import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -155,6 +160,10 @@ private List dispatchRequest(Tuple tuple, BaseRequest reques result = getSwitchProperties(); } else if (request instanceof GetSwitchLagPortsRequest) { result = getLagPorts((GetSwitchLagPortsRequest) request); + } else if (request instanceof GetSwitchLacpStatusRequest) { + result = getSwitchLacpStatus((GetSwitchLacpStatusRequest) request); + } else if (request instanceof GetLacpStatusRequest) { + result = getLacpStatus((GetLacpStatusRequest) request); } else if (request instanceof GetFlowsPerPortForSwitchRequest) { result = getSwitchFlows((GetFlowsPerPortForSwitchRequest) request); } else { @@ -325,6 +334,34 @@ private List getLagPorts(GetSwitchLagPortsRequest request } } + private List getSwitchLacpStatus(GetSwitchLacpStatusRequest request) { + try { + return switchOperationsService.getSwitchLacpStatus(request.getSwitchId()).stream() + .map(LacpStatusMapper.INSTANCE::map) + .map(SwitchLacpStatusResponse::new) + .collect(Collectors.toList()); + } catch (SwitchNotFoundException e) { + throw new MessageException(ErrorType.NOT_FOUND, e.getMessage(), + "Could not get LACP Status for non existent switch"); + } + } + + private List getLacpStatus(GetLacpStatusRequest request) { + try { + Optional switchLacpStatusResponseOptional = switchOperationsService + .getLacpStatus(request.getSwitchId(), request.getLogicalPortNumber()) + .map(LacpStatusMapper.INSTANCE::map) + .map(SwitchLacpStatusResponse::new); + + return switchLacpStatusResponseOptional.isPresent() + ? Collections.singletonList(switchLacpStatusResponseOptional.get()) + : Collections.EMPTY_LIST; + } catch (SwitchNotFoundException e) { + throw new MessageException(ErrorType.NOT_FOUND, e.getMessage(), + "Could not get LACP Status for non existent switch"); + } + } + private GetSwitchResponse patchSwitch(SwitchPatchRequest request) { try { return new GetSwitchResponse( @@ -415,3 +452,4 @@ public void islBfdPropertiesChanged(Endpoint source, Endpoint destination) { log.warn("Discard islBfdPropertiesChanged link carrier call"); } } + diff --git a/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/topology/nbworker/services/SwitchOperationsService.java b/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/topology/nbworker/services/SwitchOperationsService.java index 97afc52990f..99a95897c1e 100644 --- a/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/topology/nbworker/services/SwitchOperationsService.java +++ b/src-java/nbworker-topology/nbworker-storm-topology/src/main/java/org/openkilda/wfm/topology/nbworker/services/SwitchOperationsService.java @@ -34,6 +34,7 @@ import org.openkilda.model.Isl; import org.openkilda.model.IslEndpoint; import org.openkilda.model.IslStatus; +import org.openkilda.model.LacpPartner; import org.openkilda.model.LagLogicalPort; import org.openkilda.model.PhysicalPort; import org.openkilda.model.PortProperties; @@ -49,6 +50,7 @@ import org.openkilda.persistence.repositories.FlowPathRepository; import org.openkilda.persistence.repositories.FlowRepository; import org.openkilda.persistence.repositories.IslRepository; +import org.openkilda.persistence.repositories.LacpPartnerRepository; import org.openkilda.persistence.repositories.LagLogicalPortRepository; import org.openkilda.persistence.repositories.PhysicalPortRepository; import org.openkilda.persistence.repositories.PortPropertiesRepository; @@ -102,6 +104,7 @@ public class SwitchOperationsService { private FlowPathRepository flowPathRepository; private PhysicalPortRepository physicalPortRepository; private PortRepository portRepository; + private LacpPartnerRepository lacpPartnerRepository; public SwitchOperationsService( RepositoryFactory repositoryFactory, TransactionManager transactionManager, @@ -122,6 +125,7 @@ public SwitchOperationsService( this.flowMirrorPathRepository = repositoryFactory.createFlowMirrorPathRepository(); this.physicalPortRepository = repositoryFactory.createPhysicalPortRepository(); this.portRepository = repositoryFactory.createPortRepository(); + this.lacpPartnerRepository = repositoryFactory.createLacpPartnerRepository(); this.carrier = carrier; } @@ -152,7 +156,7 @@ public List getAllSwitches() { /** * Update the "Under maintenance" flag for the switch. * - * @param switchId switch id. + * @param switchId switch id. * @param underMaintenance "Under maintenance" flag. * @return updated switch. * @throws SwitchNotFoundException if there is no switch with this switch id. @@ -197,8 +201,8 @@ public Switch updateSwitchUnderMaintenanceFlag(SwitchId switchId, boolean underM * Delete switch. * * @param switchId ID of switch to be deleted - * @param force if True all switch relationships will be deleted too. - * If False switch will be deleted only if it has no relations. + * @param force if True all switch relationships will be deleted too. + * If False switch will be deleted only if it has no relations. * @return True if switch was deleted, False otherwise * @throws SwitchNotFoundException if switch is not found */ @@ -232,7 +236,7 @@ public boolean deleteSwitch(SwitchId switchId, boolean force) throws SwitchNotFo /** * Check that switch is not in 'Active' state. * - * @throws SwitchNotFoundException if there is no such switch. + * @throws SwitchNotFoundException if there is no such switch. * @throws IllegalSwitchStateException if switch is in 'Active' state */ public void checkSwitchIsDeactivated(SwitchId switchId) @@ -333,9 +337,9 @@ public List getSwitchProperties() { /** * Update switch properties. * - * @param switchId target switch id + * @param switchId target switch id * @param switchPropertiesDto switch properties - * @throws IllegalSwitchPropertiesException if switch properties are incorrect + * @throws IllegalSwitchPropertiesException if switch properties are incorrect * @throws SwitchPropertiesNotFoundException if switch properties is not found by switch id */ public SwitchPropertiesDto updateSwitchProperties(SwitchId switchId, SwitchPropertiesDto switchPropertiesDto) { @@ -449,10 +453,10 @@ private void validateSwitchProperties(SwitchId switchId, SwitchProperties update if (!flowsWitchEnabledLldp.isEmpty()) { throw new IllegalSwitchPropertiesException( format("Illegal switch properties combination for switch %s. " - + "Detect Connected Devices feature is turn on for following flows [%s]. " - + "For correct work of this feature switch property 'multiTable' must be set to 'true' " - + "Please disable detecting of connected devices via LLDP for each flow before set " - + "'multiTable' property to 'false'", + + "Detect Connected Devices feature via LLDP is turned on for following flows [%s]." + + "For correct work of this feature, switch property 'multiTable' must be set to 'true' " + + "Please disable detecting of connected devices via LLDP for each flow before setting " + + "'multiTable' property to 'false'", switchId, String.join(", ", flowsWitchEnabledLldp))); } @@ -463,10 +467,10 @@ private void validateSwitchProperties(SwitchId switchId, SwitchProperties update if (!flowsWithEnabledArp.isEmpty()) { throw new IllegalSwitchPropertiesException( format("Illegal switch properties combination for switch %s. " - + "Detect Connected Devices feature via ARP is turn on for following flows [%s]. " - + "For correct work of this feature switch property 'multiTable' must be set to 'true' " - + "Please disable detecting of connected devices via ARP for each flow before set " - + "'multiTable' property to 'false'", + + "Detect Connected Devices feature via ARP is turn on for following flows [%s]. " + + "For correct work of this feature switch property 'multiTable' must be set to 'true' " + + "Please disable detecting of connected devices via ARP for each flow before set " + + "'multiTable' property to 'false'", switchId, String.join(", ", flowsWithEnabledArp))); } } @@ -541,7 +545,7 @@ private void validateSwitchProperties(SwitchId switchId, SwitchProperties update * Get port properties. * * @param switchId target switch id - * @param port port number + * @param port port number */ public PortProperties getPortProperties(SwitchId switchId, int port) throws SwitchNotFoundException { return portPropertiesRepository.getBySwitchIdAndPort(switchId, port) @@ -582,6 +586,36 @@ public Collection getSwitchLagPorts(SwitchId switchId) throws Sw }); } + /** + * Get switch LACP status. + * + * @param switchId target switch id + */ + public Collection getSwitchLacpStatus(SwitchId switchId) throws SwitchNotFoundException { + return transactionManager.doInTransaction(() -> { + if (!switchRepository.exists(switchId)) { + throw new SwitchNotFoundException(switchId); + } + return lacpPartnerRepository.findBySwitchId(switchId); + }); + } + + /** + * Get LACP status. + * + * @param switchId target switch id + * @param logicalPortNumber target logical port number + */ + public Optional getLacpStatus(SwitchId switchId, int logicalPortNumber) + throws SwitchNotFoundException { + return transactionManager.doInTransaction(() -> { + if (!switchRepository.exists(switchId)) { + throw new SwitchNotFoundException(switchId); + } + return lacpPartnerRepository.findBySwitchIdAndLogicalPortNumber(switchId, logicalPortNumber); + }); + } + /** * Find and return all {@code IslEndpoint} for all ISL detected for this switch. */ diff --git a/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v2/switches/LacpStatusResponse.java b/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v2/switches/LacpStatusResponse.java new file mode 100644 index 00000000000..7f4414c35f2 --- /dev/null +++ b/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v2/switches/LacpStatusResponse.java @@ -0,0 +1,47 @@ +/* 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.dto.v2.switches; + +import org.openkilda.model.MacAddress; +import org.openkilda.model.SwitchId; + +import com.fasterxml.jackson.databind.PropertyNamingStrategy; +import com.fasterxml.jackson.databind.annotation.JsonNaming; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@NoArgsConstructor +@AllArgsConstructor +@JsonNaming(PropertyNamingStrategy.SnakeCaseStrategy.class) +public class LacpStatusResponse { + SwitchId switchId; + int logicalPortNumber; + int systemPriority; + MacAddress systemId; + int key; + int portPriority; + int portNumber; + boolean stateActive; + boolean stateShortTimeout; + boolean stateAggregatable; + boolean stateSynchronised; + boolean stateCollecting; + boolean stateDistributing; + boolean stateDefaulted; + boolean stateExpired; +} diff --git a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/controller/v2/SwitchControllerV2.java b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/controller/v2/SwitchControllerV2.java index 1cda7ea0b3e..13355ddf130 100644 --- a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/controller/v2/SwitchControllerV2.java +++ b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/controller/v2/SwitchControllerV2.java @@ -19,6 +19,7 @@ import org.openkilda.messaging.error.MessageException; import org.openkilda.model.SwitchId; import org.openkilda.northbound.controller.BaseController; +import org.openkilda.northbound.dto.v2.switches.LacpStatusResponse; import org.openkilda.northbound.dto.v2.switches.LagPortRequest; import org.openkilda.northbound.dto.v2.switches.LagPortResponse; import org.openkilda.northbound.dto.v2.switches.PortHistoryResponse; @@ -83,10 +84,10 @@ public CompletableFuture> getPortHistory( @PathVariable("port") int port, @ApiParam(value = "default: the day before timeTo.") @RequestParam(value = "timeFrom", required = false) @DateTimeFormat(iso = ISO.DATE_TIME) - Optional optionalFrom, + Optional optionalFrom, @ApiParam(value = "default: now.") @RequestParam(value = "timeTo", required = false) @DateTimeFormat(iso = ISO.DATE_TIME) - Optional optionalTo) { + Optional optionalTo) { Instant timeTo = optionalTo.map(Date::toInstant).orElseGet(Instant::now); Instant timeFrom = optionalFrom.map(Date::toInstant).orElseGet(() -> timeTo.minus(1, ChronoUnit.DAYS)); @@ -216,6 +217,36 @@ public CompletableFuture> getLagPorts(@PathVariable("switc return switchService.getLagPorts(switchId); } + /** + * Get LACP status on Switch. + * + * @param switchId the switch + */ + @ApiOperation(value = "Read all LACP status values/fields, on a specific switch", + response = LacpStatusResponse.class) + @GetMapping(value = "/{switch_id}/lacpstatus") + @ResponseStatus(HttpStatus.OK) + public CompletableFuture> getLacpStatus(@PathVariable("switch_id") SwitchId switchId) { + return switchService.getLacpStatus(switchId); + } + + /** + * Get LACP status. + * + * @param switchId the switch + * @param logicalPortNumber the switch + * + */ + @ApiOperation(value = "Read LACP status values/fields, on a specific port of the switch", + response = LacpStatusResponse.class) + @GetMapping(value = "/{switch_id}/lacp/{logical_port_number}") + @ResponseStatus(HttpStatus.OK) + public CompletableFuture getLacpStatus(@PathVariable("switch_id") SwitchId switchId, + @PathVariable("logical_port_number") + int logicalPortNumber) { + return switchService.getLacpStatus(switchId, logicalPortNumber); + } + /** * Update LAG logical port. */ diff --git a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/converter/LacpStatusMapper.java b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/converter/LacpStatusMapper.java new file mode 100644 index 00000000000..91a242da12c --- /dev/null +++ b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/converter/LacpStatusMapper.java @@ -0,0 +1,27 @@ +/* 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.converter; + +import org.openkilda.messaging.nbtopology.response.LacpStatusDto; +import org.openkilda.northbound.dto.v2.switches.LacpStatusResponse; + +import org.mapstruct.Mapper; + +@Mapper(componentModel = "spring") +public interface LacpStatusMapper { + + LacpStatusResponse map(LacpStatusDto response); +} diff --git a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/service/SwitchService.java b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/service/SwitchService.java index ba4edd7a608..71b7076a8f9 100644 --- a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/service/SwitchService.java +++ b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/service/SwitchService.java @@ -35,6 +35,7 @@ import org.openkilda.northbound.dto.v1.switches.SwitchSyncResult; import org.openkilda.northbound.dto.v1.switches.SwitchValidationResult; import org.openkilda.northbound.dto.v1.switches.UnderMaintenanceDto; +import org.openkilda.northbound.dto.v2.switches.LacpStatusResponse; import org.openkilda.northbound.dto.v2.switches.LagPortRequest; import org.openkilda.northbound.dto.v2.switches.LagPortResponse; import org.openkilda.northbound.dto.v2.switches.PortHistoryResponse; @@ -326,6 +327,10 @@ CompletableFuture updatePortProperties(SwitchId switchId CompletableFuture> getLagPorts(SwitchId switchId); + CompletableFuture> getLacpStatus(SwitchId switchId); + + CompletableFuture getLacpStatus(SwitchId switchId, int logicalPortNumber); + CompletableFuture updateLagPort( SwitchId switchId, int logicalPortNumber, LagPortRequest payload); diff --git a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/service/impl/SwitchServiceImpl.java b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/service/impl/SwitchServiceImpl.java index 6a592137598..aa980a3f3e7 100644 --- a/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/service/impl/SwitchServiceImpl.java +++ b/src-java/northbound-service/northbound/src/main/java/org/openkilda/northbound/service/impl/SwitchServiceImpl.java @@ -58,8 +58,10 @@ import org.openkilda.messaging.nbtopology.request.GetAllSwitchPropertiesRequest; import org.openkilda.messaging.nbtopology.request.GetFlowsForSwitchRequest; import org.openkilda.messaging.nbtopology.request.GetFlowsPerPortForSwitchRequest; +import org.openkilda.messaging.nbtopology.request.GetLacpStatusRequest; import org.openkilda.messaging.nbtopology.request.GetPortPropertiesRequest; import org.openkilda.messaging.nbtopology.request.GetSwitchConnectedDevicesRequest; +import org.openkilda.messaging.nbtopology.request.GetSwitchLacpStatusRequest; import org.openkilda.messaging.nbtopology.request.GetSwitchLagPortsRequest; import org.openkilda.messaging.nbtopology.request.GetSwitchPropertiesRequest; import org.openkilda.messaging.nbtopology.request.GetSwitchRequest; @@ -73,6 +75,7 @@ import org.openkilda.messaging.nbtopology.response.DeleteSwitchResponse; import org.openkilda.messaging.nbtopology.response.GetFlowsPerPortForSwitchResponse; import org.openkilda.messaging.nbtopology.response.GetSwitchResponse; +import org.openkilda.messaging.nbtopology.response.SwitchLacpStatusResponse; import org.openkilda.messaging.nbtopology.response.SwitchLagPortResponse; import org.openkilda.messaging.nbtopology.response.SwitchPropertiesResponse; import org.openkilda.messaging.payload.flow.FlowPayload; @@ -87,6 +90,7 @@ import org.openkilda.model.SwitchId; import org.openkilda.northbound.converter.ConnectedDeviceMapper; import org.openkilda.northbound.converter.FlowMapper; +import org.openkilda.northbound.converter.LacpStatusMapper; import org.openkilda.northbound.converter.LagPortMapper; import org.openkilda.northbound.converter.PortPropertiesMapper; import org.openkilda.northbound.converter.SwitchMapper; @@ -100,6 +104,7 @@ import org.openkilda.northbound.dto.v1.switches.SwitchSyncResult; import org.openkilda.northbound.dto.v1.switches.SwitchValidationResult; import org.openkilda.northbound.dto.v1.switches.UnderMaintenanceDto; +import org.openkilda.northbound.dto.v2.switches.LacpStatusResponse; import org.openkilda.northbound.dto.v2.switches.LagPortRequest; import org.openkilda.northbound.dto.v2.switches.LagPortResponse; import org.openkilda.northbound.dto.v2.switches.PortHistoryResponse; @@ -149,6 +154,9 @@ public class SwitchServiceImpl extends BaseService implements SwitchService { @Autowired private LagPortMapper lagPortMapper; + @Autowired + private LacpStatusMapper lacpStatusMapper; + @Autowired private ConnectedDeviceMapper connectedDeviceMapper; @@ -531,8 +539,8 @@ public CompletableFuture getFlowsPerPortForSwitch(Sw CommandMessage message = new CommandMessage(data, System.currentTimeMillis(), correlationId); return messagingChannel.sendAndGet(nbworkerTopic, message) - .thenApply(GetFlowsPerPortForSwitchResponse.class::cast) - .thenApply(switchMapper::toSwitchFlowsPerPortResponseV2Api); + .thenApply(GetFlowsPerPortForSwitchResponse.class::cast) + .thenApply(switchMapper::toSwitchFlowsPerPortResponseV2Api); } @@ -698,6 +706,34 @@ public CompletableFuture> getLagPorts(SwitchId switchId) { .collect(Collectors.toList())); } + @Override + public CompletableFuture> getLacpStatus(SwitchId switchId) { + log.info("API request: Getting LACP status on switch {}", switchId); + + GetSwitchLacpStatusRequest data = new GetSwitchLacpStatusRequest(switchId); + CommandMessage request = new CommandMessage(data, System.currentTimeMillis(), RequestCorrelationId.getId()); + + return messagingChannel.sendAndGetChunked(nbworkerTopic, request) + .thenApply(result -> result.stream() + .map(SwitchLacpStatusResponse.class::cast) + .map(SwitchLacpStatusResponse::getData) + .map(lacpStatusMapper::map) + .collect(Collectors.toList())); + } + + @Override + public CompletableFuture getLacpStatus(SwitchId switchId, int logicalPortNumber) { + log.info("API request: Read LACP status on specific port {} of the switch {}", logicalPortNumber, switchId); + + GetLacpStatusRequest data = new GetLacpStatusRequest(switchId, logicalPortNumber); + CommandMessage request = new CommandMessage(data, System.currentTimeMillis(), RequestCorrelationId.getId()); + + return messagingChannel.sendAndGet(nbworkerTopic, request) + .thenApply(SwitchLacpStatusResponse.class::cast) + .thenApply(SwitchLacpStatusResponse::getData) + .thenApply(lacpStatusMapper::map); + } + @Override public CompletableFuture updateLagPort( SwitchId switchId, int logicalPortNumber, LagPortRequest payload) { @@ -783,3 +819,5 @@ private Set parseAndValidateFilters(String query, Set