diff --git a/CHANGELOG.md b/CHANGELOG.md index 0007af43288..2debbe287ab 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,34 @@ # Changelog +## v1.118.3 (26/04/2022) + + +### Bug Fixes: +- [#4768](https://github.com/telstra/open-kilda/pull/4768) Correct removing of LLDP/ARP/Server42 rules during update in swap endpoint case (Issue: [#4766](https://github.com/telstra/open-kilda/issues/4766)) +- [#4790](https://github.com/telstra/open-kilda/pull/4790) Fix groups install in switch sync operation [**floodlight**] +- [#4767](https://github.com/telstra/open-kilda/pull/4767) Fix flow path endpoints in flow dump for update flow operation (Issue: [#4606](https://github.com/telstra/open-kilda/issues/4606)) + +### Improvements: +- [#4780](https://github.com/telstra/open-kilda/pull/4780) Unignore test according to #4733 (Issues: [#3973](https://github.com/telstra/open-kilda/issues/3973) [#4733](https://github.com/telstra/open-kilda/issues/4733)) [**tests**] +- [#4751](https://github.com/telstra/open-kilda/pull/4751) [test] unignore tests for #4607 (Issue: [#4607](https://github.com/telstra/open-kilda/issues/4607)) +- [#4753](https://github.com/telstra/open-kilda/pull/4753) Rework switch sync to use RuleManager [**floodlight**][**storm-topologies**] +- [#4786](https://github.com/telstra/open-kilda/pull/4786) update makefile template according to #4734 (Issue: [#4734](https://github.com/telstra/open-kilda/issues/4734)) +- [#4754](https://github.com/telstra/open-kilda/pull/4754) Added config option for spout parallelism [**storm-topologies**] +- [#4757](https://github.com/telstra/open-kilda/pull/4757) Fix appearing warnings for FlowCrudSpec [**tests**] +- [#4734](https://github.com/telstra/open-kilda/pull/4734) Making repeatable builds possible w/o Internet + +### Other changes: +- [#4745](https://github.com/telstra/open-kilda/pull/4745) Switch validation without logical port info (Issue: [#4574](https://github.com/telstra/open-kilda/issues/4574)) [**northbound**][**storm-topologies**] +- [#4778](https://github.com/telstra/open-kilda/pull/4778) Add test to validate non existing lag port creation [**tests**] +- [#4791](https://github.com/telstra/open-kilda/pull/4791) Revert "Use MySQL DB to store history on local env" (Issue: [#4746](https://github.com/telstra/open-kilda/issues/4746)) + +For the complete list of changes, check out [the commit log](https://github.com/telstra/open-kilda/compare/v1.118.2...v1.118.3). + +### Affected Components: +otsdb, network, router, ping, reroute, fl, flow-hs, history, swmanager, nb, nbworker, flow-monitor + + + ## v1.118.2 (08/04/2022) ### Bug Fixes: diff --git a/confd/templates/flowhs-topology/flowhs-topology.tmpl b/confd/templates/flowhs-topology/flowhs-topology.tmpl index 4ef59560eeb..75ac1667468 100644 --- a/confd/templates/flowhs-topology/flowhs-topology.tmpl +++ b/confd/templates/flowhs-topology/flowhs-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: {{ getv "/kilda_storm_flow_hs_parallelism" }} topology.workers: {{ getv "/kilda_storm_flowhs_workers" }} + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: @@ -10,7 +11,7 @@ spouts: - id: "zookeeper.spout" parallelism: 1 - id: "FLOW_SPOUT" - parallelism: {{ getv "/kilda_storm_flow_hs_parallelism" }} + parallelism: {{ getv "/kilda_storm_spout_parallelism" }} properties: # The ratio of FLOW_SPOUT max pending to FLOW_xxx_HUB parallelism defines backpressure for H&S hubs. # diff --git a/confd/templates/flowmonitoring-topology/flowmonitoring-topology.tmpl b/confd/templates/flowmonitoring-topology/flowmonitoring-topology.tmpl index caea37ef0f1..942960d901f 100644 --- a/confd/templates/flowmonitoring-topology/flowmonitoring-topology.tmpl +++ b/confd/templates/flowmonitoring-topology/flowmonitoring-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: {{ getv "/kilda_storm_flow_monitoring_parallelism" }} topology.workers: {{ getv "/kilda_storm_parallelism_workers_count" }} + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: diff --git a/confd/templates/history-topology/history-topology.tmpl b/confd/templates/history-topology/history-topology.tmpl index e7ecbceab49..34be5da355d 100644 --- a/confd/templates/history-topology/history-topology.tmpl +++ b/confd/templates/history-topology/history-topology.tmpl @@ -2,7 +2,7 @@ config: topology.parallelism: {{ getv "/kilda_storm_history_parallelism" }} topology.workers: {{ getv "/kilda_storm_parallelism_workers_count" }} - topology.spouts.parallelism: 1 + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: diff --git a/confd/templates/makefile/makefile.tmpl b/confd/templates/makefile/makefile.tmpl index 96d285c1c08..465db57bca6 100644 --- a/confd/templates/makefile/makefile.tmpl +++ b/confd/templates/makefile/makefile.tmpl @@ -85,7 +85,7 @@ clean-docker-files: .PHONY: clean-sources clean-sources: -{{if not (exists "/no_gui")}} $(MAKE) -C src-gui clean clean-java +{{if not (exists "/no_gui")}} $(MAKE) -C src-gui clean-java {{end}} $(MAKE) -C src-python/lab-service/lab clean cd src-java && ./gradlew clean diff --git a/confd/templates/nbworker-topology/nbworker-topology.tmpl b/confd/templates/nbworker-topology/nbworker-topology.tmpl index 058d07617d5..4016eb3f4c0 100644 --- a/confd/templates/nbworker-topology/nbworker-topology.tmpl +++ b/confd/templates/nbworker-topology/nbworker-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: {{ getv "/kilda_storm_parallelism_level_new" }} topology.workers: {{ getv "/kilda_storm_parallelism_workers_count" }} + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: diff --git a/confd/templates/network-topology/network-topology.tmpl b/confd/templates/network-topology/network-topology.tmpl index 15078b42d18..c8e54231c79 100644 --- a/confd/templates/network-topology/network-topology.tmpl +++ b/confd/templates/network-topology/network-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: 2 topology.workers: {{ getv "/kilda_storm_parallelism_workers_count" }} + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: diff --git a/confd/templates/opentsdb-topology/opentsdb-topology.tmpl b/confd/templates/opentsdb-topology/opentsdb-topology.tmpl index 60efa00a9cb..1b649214772 100644 --- a/confd/templates/opentsdb-topology/opentsdb-topology.tmpl +++ b/confd/templates/opentsdb-topology/opentsdb-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: {{ getv "/kilda_storm_parallelism_level" }} topology.workers: {{ getv "/kilda_opentsdb_workers" }} + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: diff --git a/confd/templates/ping-topology/ping-topology.tmpl b/confd/templates/ping-topology/ping-topology.tmpl index 456e99ad760..acc59e9ed14 100644 --- a/confd/templates/ping-topology/ping-topology.tmpl +++ b/confd/templates/ping-topology/ping-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: 2 topology.workers: {{ getv "/kilda_storm_parallelism_workers_count" }} + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: diff --git a/confd/templates/reroute-topology/reroute-topology.tmpl b/confd/templates/reroute-topology/reroute-topology.tmpl index 176c9858c67..aa2dc0ccca5 100644 --- a/confd/templates/reroute-topology/reroute-topology.tmpl +++ b/confd/templates/reroute-topology/reroute-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: {{ getv "/kilda_storm_parallelism_level_new" }} topology.workers: {{ getv "/kilda_storm_parallelism_workers_count" }} + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: diff --git a/confd/templates/server42/server42-control-topology.tmpl b/confd/templates/server42/server42-control-topology.tmpl index 86851279e27..7fe0783583a 100644 --- a/confd/templates/server42/server42-control-topology.tmpl +++ b/confd/templates/server42/server42-control-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: {{ getv "/kilda_storm_parallelism_level_new" }} topology.workers: {{ getv "/kilda_storm_parallelism_workers_count" }} + topology.spouts.parallelism: {{ getv "/kilda_storm_spout_parallelism" }} # spout definitions spouts: diff --git a/confd/templates/stats-topology/stats-topology.tmpl b/confd/templates/stats-topology/stats-topology.tmpl index 7b47734d39c..8418e410ec8 100644 --- a/confd/templates/stats-topology/stats-topology.tmpl +++ b/confd/templates/stats-topology/stats-topology.tmpl @@ -2,6 +2,7 @@ config: topology.parallelism: {{ getv "/kilda_storm_parallelism_level" }} topology.workers: {{ getv "/kilda_storm_parallelism_workers_count" }} + topology.spouts.parallelism: 1 # spout definitions spouts: diff --git a/confd/vars/main.yaml b/confd/vars/main.yaml index f8d0bdc5f10..6d761e44a6b 100644 --- a/confd/vars/main.yaml +++ b/confd/vars/main.yaml @@ -173,6 +173,8 @@ kilda_storm_flowhs_workers: 1 kilda_storm_parallelism_workers_count: 1 kilda_storm_history_parallelism: 2 +kilda_storm_spout_parallelism: 2 + kilda_storm_flow_monitoring_parallelism: 2 kilda_storm_disruptor_wait_timeout: 1000 diff --git a/docker/db-migration/Dockerfile b/docker/db-migration/Dockerfile index de7a43ee1c4..f13146b3a17 100644 --- a/docker/db-migration/Dockerfile +++ b/docker/db-migration/Dockerfile @@ -50,19 +50,32 @@ RUN GNUPGHOME="$(mktemp -d)" # Download JDBC libraries and plugins -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/com/orientechnologies/orientdb-jdbc/3.0.34/orientdb-jdbc-3.0.34-all.jar /liquibase/lib/orientdb-jdbc-3.0.34-all.jar -#ADD --chown=liquibase:liquibase https://dl.bintray.com/till-krullmann/tools/org/unbroken-dome/liquibase-orientdb/liquibase-orientdb/0.3.0/liquibase-orientdb-0.3.0.jar /liquibase/lib/liquibase-orientdb-0.3.0.jar -COPY --chown=liquibase:liquibase lib/liquibase-orientdb-0.3.0.jar /liquibase/lib/liquibase-orientdb-0.3.0.jar +RUN set -x \ + && mkdir -p /liquibase/lib/ \ + && wget -O /liquibase/lib/orientdb-jdbc-3.0.34-all.jar \ + "https://repo1.maven.org/maven2/com/orientechnologies/orientdb-jdbc/3.0.34/orientdb-jdbc-3.0.34-all.jar" \ + # && wget -O /liquibase/lib/liquibase-orientdb-0.3.0.jar \ + # "https://dl.bintray.com/till-krullmann/tools/org/unbroken-dome/liquibase-orientdb/liquibase-orientdb/0.3.0/liquibase-orientdb-0.3.0.jar" \ + # black magic satisfying dependencies of `liquibase-orientdb-0.3.0.jar` + && wget -O /liquibase/lib/commons-lang3-3.6.jar \ + "https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.6/commons-lang3-3.6.jar" \ + && wget -O /liquibase/lib/commons-beanutils-1.9.2.jar \ + "https://repo1.maven.org/maven2/commons-beanutils/commons-beanutils/1.9.2/commons-beanutils-1.9.2.jar" \ + && wget -O /liquibase/lib/guava-23.3-jre.jar \ + "https://repo1.maven.org/maven2/com/google/guava/guava/23.3-jre/guava-23.3-jre.jar" \ + && wget -O /liquibase/lib/validation-api-1.1.0.Final.jar \ + "https://repo1.maven.org/maven2/javax/validation/validation-api/1.1.0.Final/validation-api-1.1.0.Final.jar" \ + && wget -O /liquibase/lib/hibernate-validator-5.2.4.Final.jar \ + "https://repo1.maven.org/maven2/org/hibernate/hibernate-validator/5.2.4.Final/hibernate-validator-5.2.4.Final.jar" \ + && wget -O /liquibase/lib/jackson-databind-2.6.7.jar \ + "https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.6.7/jackson-databind-2.6.7.jar" \ + && wget -O /liquibase/lib/jboss-logging-3.2.1.Final.jar \ + "https://repo1.maven.org/maven2/org/jboss/logging/jboss-logging/3.2.1.Final/jboss-logging-3.2.1.Final.jar" \ + && wget -O /liquibase/lib/classmate-1.1.0.jar \ + "https://repo1.maven.org/maven2/com/fasterxml/classmate/1.1.0/classmate-1.1.0.jar" \ + && chown -R liquibase:liquibase /liquibase/lib/ -# black magic satisfying dependencies of `liquibase-orientdb-0.3.0.jar` -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/org/apache/commons/commons-lang3/3.6/commons-lang3-3.6.jar /liquibase/lib/commons-lang3-3.6.jar -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/commons-beanutils/commons-beanutils/1.9.2/commons-beanutils-1.9.2.jar /liquibase/lib/commons-beanutils-1.9.2.jar -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/com/google/guava/guava/23.3-jre/guava-23.3-jre.jar /liquibase/lib/guava-23.3-jre.jar -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/javax/validation/validation-api/1.1.0.Final/validation-api-1.1.0.Final.jar /liquibase/lib/validation-api-1.1.0.Final.jar -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/org/hibernate/hibernate-validator/5.2.4.Final/hibernate-validator-5.2.4.Final.jar /liquibase/lib/hibernate-validator-5.2.4.Final.jar -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/com/fasterxml/jackson/core/jackson-databind/2.6.7/jackson-databind-2.6.7.jar /liquibase/lib/jackson-databind-2.6.7.jar -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/org/jboss/logging/jboss-logging/3.2.1.Final/jboss-logging-3.2.1.Final.jar /liquibase/lib/jboss-logging-3.2.1.Final.jar -ADD --chown=liquibase:liquibase https://repo1.maven.org/maven2/com/fasterxml/classmate/1.1.0/classmate-1.1.0.jar /liquibase/lib/classmate-1.1.0.jar +COPY --chown=liquibase:liquibase lib/liquibase-orientdb-0.3.0.jar /liquibase/lib/liquibase-orientdb-0.3.0.jar ENV KILDA_ORIENTDB_USER=kilda ENV KILDA_ORIENTDB_PASSWORD=kilda diff --git a/docs/design/hub-and-spoke/switch-sync/h&s-switch-sync.png b/docs/design/hub-and-spoke/switch-sync/h&s-switch-sync.png index c5606153d14..e6773bf5d12 100644 Binary files a/docs/design/hub-and-spoke/switch-sync/h&s-switch-sync.png and b/docs/design/hub-and-spoke/switch-sync/h&s-switch-sync.png differ diff --git a/docs/design/hub-and-spoke/switch-sync/h&s-switch-sync.puml b/docs/design/hub-and-spoke/switch-sync/h&s-switch-sync.puml index 319cc98b37e..903f121609e 100644 --- a/docs/design/hub-and-spoke/switch-sync/h&s-switch-sync.puml +++ b/docs/design/hub-and-spoke/switch-sync/h&s-switch-sync.puml @@ -5,9 +5,7 @@ actor User boundary Northbound as NB participant SwitchManager << Hub >> participant SpeakerWorker -participant CommandBuilder participant Floodlight as FL -database DAO User -> NB : Switch sync rules @@ -18,26 +16,18 @@ activate SwitchManager SwitchManager -> SwitchManager : Switch validate opt Missing rules exists - SwitchManager -> CommandBuilder : buildCommandsToCreateMissingRules - activate CommandBuilder - CommandBuilder -> CommandBuilder : Build commands - CommandBuilder -> SwitchManager : Rule installation commands - deactivate CommandBuilder + SwitchManager -> SwitchManager : Rule installation commands end opt Excess rules exists and removeExcess=true - SwitchManager -> CommandBuilder : buildCommandsToRemoveExcessRules - activate CommandBuilder - CommandBuilder -> CommandBuilder : Build commands - CommandBuilder -> SwitchManager : Rule remove commands - deactivate CommandBuilder + SwitchManager -> SwitchManager : Rule remove commands end group Sending rules commands opt Missing rules commands exists loop for each missing rule - SwitchManager ->> SpeakerWorker : InstallFlowForSwitchManagerRequest - SpeakerWorker ->> FL : InstallFlowForSwitchManagerRequest + SwitchManager ->> SpeakerWorker : BaseSpeakerCommandsRequest + SpeakerWorker ->> FL : BaseSpeakerCommandsRequest FL ->> SpeakerWorker SpeakerWorker ->> SwitchManager @@ -45,8 +35,8 @@ group Sending rules commands end opt Excess rules commands exists loop for each excess rule - SwitchManager ->> SpeakerWorker : RemoveFlowForSwitchManagerRequest - SpeakerWorker ->> FL : RemoveFlowForSwitchManagerRequest + SwitchManager ->> SpeakerWorker : DeleteSpeakerCommandsRequest + SpeakerWorker ->> FL : DeleteSpeakerCommandsRequest FL ->> SpeakerWorker SpeakerWorker ->> SwitchManager end @@ -60,8 +50,8 @@ end opt Excess meters commands exists loop for each excess meter - SwitchManager ->> SpeakerWorker : DeleterMeterForSwitchManagerRequest - SpeakerWorker ->> FL : DeleterMeterForSwitchManagerRequest + SwitchManager ->> SpeakerWorker : DeleteSpeakerCommandsRequest + SpeakerWorker ->> FL : DeleteSpeakerCommandsRequest FL ->> SpeakerWorker SpeakerWorker --> SwitchManager end diff --git a/docs/design/hub-and-spoke/switch-sync/switch-sync-fsm.dot b/docs/design/hub-and-spoke/switch-sync/switch-sync-fsm.dot index 0e16ad589b1..34138909216 100644 --- a/docs/design/hub-and-spoke/switch-sync/switch-sync-fsm.dot +++ b/docs/design/hub-and-spoke/switch-sync/switch-sync-fsm.dot @@ -4,29 +4,27 @@ subgraph cluster_StateMachine { label="org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm"; COMPUTE_EXCESS_RULES [label="COMPUTE_EXCESS_RULES"]; COMPUTE_MISCONFIGURED_RULES [label="COMPUTE_MISCONFIGURED_RULES"]; -GROUPS_COMMANDS_SEND [label="GROUPS_COMMANDS_SEND"]; FINISHED_WITH_ERROR [label="FINISHED_WITH_ERROR"]; INITIALIZED [label="INITIALIZED"]; COMPUTE_LOGICAL_PORTS_COMMANDS [label="COMPUTE_LOGICAL_PORTS_COMMANDS"]; LOGICAL_PORTS_COMMANDS_SEND [label="LOGICAL_PORTS_COMMANDS_SEND"]; -RULES_COMMANDS_SEND [label="RULES_COMMANDS_SEND"]; COMPUTE_MISSING_RULES [label="COMPUTE_MISSING_RULES"]; COMPUTE_EXCESS_METERS [label="COMPUTE_EXCESS_METERS"]; COMPUTE_MISCONFIGURED_METERS [label="COMPUTE_MISCONFIGURED_METERS"]; +COMPUTE_MISSING_METERS [label="COMPUTE_MISSING_METERS"]; COMPUTE_GROUP_MIRROR_CONFIGS [label="COMPUTE_GROUP_MIRROR_CONFIGS"]; -METERS_COMMANDS_SEND [label="METERS_COMMANDS_SEND"]; +SEND_REMOVE_COMMANDS [label="SEND_REMOVE_COMMANDS"]; +SEND_MODIFY_COMMANDS [label="SEND_MODIFY_COMMANDS"]; +SEND_INSTALL_COMMANDS [label="SEND_INSTALL_COMMANDS"]; FINISHED [label="FINISHED"]; COMPUTE_EXCESS_RULES -> FINISHED_WITH_ERROR [ label="ERROR"]; COMPUTE_EXCESS_RULES -> COMPUTE_EXCESS_METERS [ label="NEXT"]; COMPUTE_MISCONFIGURED_RULES -> FINISHED_WITH_ERROR [ label="ERROR"]; COMPUTE_MISCONFIGURED_RULES -> COMPUTE_EXCESS_RULES [ label="NEXT"]; -GROUPS_COMMANDS_SEND -> GROUPS_COMMANDS_SEND [ label="GROUPS_INSTALLED"]; -GROUPS_COMMANDS_SEND -> GROUPS_COMMANDS_SEND [ label="GROUPS_MODIFIED"]; -GROUPS_COMMANDS_SEND -> GROUPS_COMMANDS_SEND [ label="GROUPS_REMOVED"]; -GROUPS_COMMANDS_SEND -> FINISHED_WITH_ERROR [ label="TIMEOUT"]; -GROUPS_COMMANDS_SEND -> FINISHED_WITH_ERROR [ label="ERROR"]; -GROUPS_COMMANDS_SEND -> METERS_COMMANDS_SEND [ label="NEXT"]; +SEND_REMOVE_COMMANDS -> FINISHED_WITH_ERROR [ label="TIMEOUT"]; +SEND_REMOVE_COMMANDS -> FINISHED_WITH_ERROR [ label="ERROR"]; +SEND_REMOVE_COMMANDS -> SEND_MODIFY_COMMANDS [ label="COMMANDS_PROCESSED"]; INITIALIZED -> COMPUTE_MISSING_RULES [ label="NEXT"]; COMPUTE_LOGICAL_PORTS_COMMANDS -> FINISHED_WITH_ERROR [ label="ERROR"]; COMPUTE_LOGICAL_PORTS_COMMANDS -> LOGICAL_PORTS_COMMANDS_SEND [ label="NEXT"]; @@ -34,23 +32,20 @@ LOGICAL_PORTS_COMMANDS_SEND -> LOGICAL_PORTS_COMMANDS_SEND [ label="LOGICAL_PORT LOGICAL_PORTS_COMMANDS_SEND -> LOGICAL_PORTS_COMMANDS_SEND [ label="LOGICAL_PORT_REMOVED"]; LOGICAL_PORTS_COMMANDS_SEND -> FINISHED_WITH_ERROR [ label="TIMEOUT"]; LOGICAL_PORTS_COMMANDS_SEND -> FINISHED_WITH_ERROR [ label="ERROR"]; -LOGICAL_PORTS_COMMANDS_SEND -> GROUPS_COMMANDS_SEND [ label="NEXT"]; -RULES_COMMANDS_SEND -> RULES_COMMANDS_SEND [ label="MISSING_RULES_INSTALLED"]; -RULES_COMMANDS_SEND -> RULES_COMMANDS_SEND [ label="EXCESS_RULES_REMOVED"]; -RULES_COMMANDS_SEND -> RULES_COMMANDS_SEND [ label="MISCONFIGURED_RULES_REINSTALLED"]; -RULES_COMMANDS_SEND -> FINISHED_WITH_ERROR [ label="TIMEOUT"]; -RULES_COMMANDS_SEND -> FINISHED_WITH_ERROR [ label="ERROR"]; -RULES_COMMANDS_SEND -> FINISHED [ label="NEXT"]; +LOGICAL_PORTS_COMMANDS_SEND -> SEND_REMOVE_COMMANDS [ label="NEXT"]; +SEND_INSTALL_COMMANDS -> FINISHED_WITH_ERROR [ label="TIMEOUT"]; +SEND_INSTALL_COMMANDS -> FINISHED_WITH_ERROR [ label="ERROR"]; +SEND_INSTALL_COMMANDS -> FINISHED [ label="COMMANDS_PROCESSED"]; COMPUTE_MISSING_RULES -> FINISHED_WITH_ERROR [ label="ERROR"]; COMPUTE_MISSING_RULES -> COMPUTE_MISCONFIGURED_RULES [ label="NEXT"]; COMPUTE_EXCESS_METERS -> FINISHED_WITH_ERROR [ label="ERROR"]; COMPUTE_EXCESS_METERS -> COMPUTE_MISCONFIGURED_METERS [ label="NEXT"]; COMPUTE_MISCONFIGURED_METERS -> FINISHED_WITH_ERROR [ label="ERROR"]; -COMPUTE_MISCONFIGURED_METERS -> COMPUTE_GROUP_MIRROR_CONFIGS [ label="NEXT"]; +COMPUTE_MISCONFIGURED_METERS -> COMPUTE_MISSING_METERS [ label="NEXT"]; +COMPUTE_MISSING_METERS -> FINISHED_WITH_ERROR [ label="ERROR"]; +COMPUTE_MISSING_METERS -> COMPUTE_GROUP_MIRROR_CONFIGS [ label="NEXT"]; COMPUTE_GROUP_MIRROR_CONFIGS -> FINISHED_WITH_ERROR [ label="ERROR"]; COMPUTE_GROUP_MIRROR_CONFIGS -> COMPUTE_LOGICAL_PORTS_COMMANDS [ label="NEXT"]; -METERS_COMMANDS_SEND -> METERS_COMMANDS_SEND [ label="METERS_REMOVED"]; -METERS_COMMANDS_SEND -> METERS_COMMANDS_SEND [ label="MISCONFIGURED_METERS_MODIFIED"]; -METERS_COMMANDS_SEND -> FINISHED_WITH_ERROR [ label="TIMEOUT"]; -METERS_COMMANDS_SEND -> FINISHED_WITH_ERROR [ label="ERROR"]; -METERS_COMMANDS_SEND -> RULES_COMMANDS_SEND [ label="NEXT"];}} \ No newline at end of file +SEND_MODIFY_COMMANDS -> FINISHED_WITH_ERROR [ label="TIMEOUT"]; +SEND_MODIFY_COMMANDS -> FINISHED_WITH_ERROR [ label="ERROR"]; +SEND_MODIFY_COMMANDS -> SEND_INSTALL_COMMANDS [ label="COMMANDS_PROCESSED"];}} diff --git a/docs/design/hub-and-spoke/switch-sync/switch-sync-fsm.png b/docs/design/hub-and-spoke/switch-sync/switch-sync-fsm.png index bfba13c7d0b..4e18bc6bf8e 100644 Binary files a/docs/design/hub-and-spoke/switch-sync/switch-sync-fsm.png and b/docs/design/hub-and-spoke/switch-sync/switch-sync-fsm.png differ diff --git a/src-gui/Makefile b/src-gui/Makefile index a8b659fc7f0..d8e627c9757 100644 --- a/src-gui/Makefile +++ b/src-gui/Makefile @@ -1,37 +1,29 @@ APP := openkilda-gui -rebuild: clean clean-java build +rebuild: clean-java build -build: clean-cache build/libs/${APP}.war +build: build/libs/${APP}.war build/libs/${APP}.war: .deps/node .deps/resources ./gradlew build -.deps/node: .deps - docker run --rm -e LOCAL_UID=`id -u $(USER)` -e LOCAL_GID=`id -g $(USER)` -v $(CURDIR)/src:/app/src -v $(CURDIR)/ui:/app/ui node:14.17-alpine \ - sh -c 'npm cache clean -f && npm install -g @angular/cli@12.0.0 --unsafe-perm && cd /app/ui && npm install && ng build --prod && chown -R $$LOCAL_UID:$$LOCAL_GID /app/src /app/ui' - touch $@ +.deps/node: | .deps + docker run --rm -e LOCAL_UID=`id -u $(USER)` -e LOCAL_GID=`id -g $(USER)` -v $(CURDIR)/src:/app/src -v $(CURDIR)/ui:/app/ui node:14.17-alpine \ + sh -c 'cd /app/ui && npm install && /app/ui/node_modules/.bin/ng build --prod && chown -R $$LOCAL_UID:$$LOCAL_GID /app' .deps/resources: .deps mkdir -p src/main/webapp/lib/css/ mkdir -p src/main/webapp/lib/javascript/ - wget -O src/main/webapp/lib/css/roboto.css https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i - wget -O src/main/webapp/lib/css/bootstrap.min.css https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css - wget -O src/main/webapp/lib/javascript/bootstrap.min.js https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js - wget -O src/main/webapp/lib/javascript/jquery-3.5.1.min.js https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js - touch $@ + [ -e src/main/webapp/lib/css/roboto.css ] || wget -O src/main/webapp/lib/css/roboto.css https://fonts.googleapis.com/css?family=Roboto:100,100i,300,300i,400,400i,500,500i,700,700i + [ -e src/main/webapp/lib/css/bootstrap.min.css ] || wget -O src/main/webapp/lib/css/bootstrap.min.css https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css + [ -e src/main/webapp/lib/javascript/bootstrap.min.js ] || wget -O src/main/webapp/lib/javascript/bootstrap.min.js https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js + [ -e src/main/webapp/lib/javascript/jquery-3.5.1.min.js ] || wget -O src/main/webapp/lib/javascript/jquery-3.5.1.min.js https://cdnjs.cloudflare.com/ajax/libs/jquery/3.5.1/jquery.js .deps: mkdir -p .deps mkdir -p tmp -clean-cache: clean - -clean: - rm -f .deps/node - rm -f .deps/resources - clean-java: ./gradlew clean -.PHONY: rebuild clean clean-java +.PHONY: rebuild clean-java diff --git a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/AbstractMessage.java b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/AbstractMessage.java index a080efd6117..0e952431e42 100644 --- a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/AbstractMessage.java +++ b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/AbstractMessage.java @@ -17,16 +17,20 @@ import static com.fasterxml.jackson.annotation.JsonTypeInfo.As.PROPERTY; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonInclude.Include; import com.fasterxml.jackson.annotation.JsonProperty; import com.fasterxml.jackson.annotation.JsonTypeInfo; import lombok.EqualsAndHashCode; import lombok.Getter; import lombok.NonNull; +import lombok.Setter; import lombok.ToString; import java.io.Serializable; @JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = PROPERTY, property = "clazz") +@JsonInclude(Include.NON_NULL) @Getter @ToString @EqualsAndHashCode @@ -36,6 +40,10 @@ public abstract class AbstractMessage implements Serializable { @JsonProperty("message_context") protected MessageContext messageContext; + @Setter + @JsonProperty("message_cookie") + protected MessageCookie messageCookie; + public AbstractMessage(@NonNull MessageContext messageContext) { this.messageContext = messageContext; } diff --git a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/switches/LogicalPortsSyncEntry.java b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/switches/LogicalPortsSyncEntry.java index 572bb39b1fe..6d12b94474e 100644 --- a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/switches/LogicalPortsSyncEntry.java +++ b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/switches/LogicalPortsSyncEntry.java @@ -33,4 +33,5 @@ public class LogicalPortsSyncEntry implements Serializable { private List excess; private List installed; private List removed; + private String error; } diff --git a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/switches/LogicalPortsValidationEntry.java b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/switches/LogicalPortsValidationEntry.java index ff8bdb8326a..c03698ecc43 100644 --- a/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/switches/LogicalPortsValidationEntry.java +++ b/src-java/base-topology/base-messaging/src/main/java/org/openkilda/messaging/info/switches/LogicalPortsValidationEntry.java @@ -31,4 +31,5 @@ public class LogicalPortsValidationEntry implements Serializable { private List misconfigured; private List proper; private List excess; + private String error; } diff --git a/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/error/UnexpectedInputException.java b/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/error/UnexpectedInputException.java index 8a00e18fdff..2766002f7f4 100644 --- a/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/error/UnexpectedInputException.java +++ b/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/error/UnexpectedInputException.java @@ -15,6 +15,7 @@ package org.openkilda.wfm.error; +import org.openkilda.messaging.AbstractMessage; import org.openkilda.messaging.Message; import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.MessageData; @@ -30,6 +31,10 @@ public UnexpectedInputException(MessageData payload) { this.input = payload; } + public UnexpectedInputException(AbstractMessage payload) { + this.input = payload; + } + public String getMessage(MessageCookie rootCookie) { return String.format("Got unexpected input data with cookie %s: %s", rootCookie, input); } diff --git a/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/share/hubandspoke/WorkerBolt.java b/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/share/hubandspoke/WorkerBolt.java index 3534485461a..0786389e075 100644 --- a/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/share/hubandspoke/WorkerBolt.java +++ b/src-java/base-topology/base-storm-topology/src/main/java/org/openkilda/wfm/share/hubandspoke/WorkerBolt.java @@ -24,12 +24,14 @@ import lombok.Builder; import lombok.Getter; +import lombok.Singular; import org.apache.storm.topology.OutputFieldsDeclarer; import org.apache.storm.tuple.Tuple; import org.apache.storm.tuple.Values; import java.io.Serializable; import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -62,7 +64,7 @@ public WorkerBolt(PersistenceManager persistenceManager, Config config) { requireNonNull(config.getStreamToHub(), "Stream to hub bolt cannot be null"); requireNonNull(config.getHubComponent(), "Hub bolt id cannot be null"); - requireNonNull(config.getWorkerSpoutComponent(), "Worker's spout id cannot be null"); + requireNonNull(config.getWorkerSpoutComponents(), "Worker's spout ids cannot be null"); this.workerConfig = config; } @@ -71,7 +73,7 @@ protected void dispatch(Tuple input) throws Exception { String sourceComponent = input.getSourceComponent(); if (workerConfig.getHubComponent().equals(sourceComponent)) { dispatchHub(input); - } else if (workerConfig.getWorkerSpoutComponent().equals(sourceComponent)) { + } else if (workerConfig.getWorkerSpoutComponents().contains(sourceComponent)) { dispatchResponse(input); } else { super.dispatch(input); @@ -182,7 +184,8 @@ public void declareOutputFields(OutputFieldsDeclarer declarer) { public static class Config implements Serializable { private String streamToHub; private String hubComponent; - private String workerSpoutComponent; + @Singular + private List workerSpoutComponents; @Builder.Default private int defaultTimeout = 100; diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/BatchCommandProcessor.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/BatchCommandProcessor.java index 4807ab74314..7aebd77b14a 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/BatchCommandProcessor.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/BatchCommandProcessor.java @@ -17,11 +17,14 @@ import org.openkilda.floodlight.api.request.rulemanager.DeleteSpeakerCommandsRequest; import org.openkilda.floodlight.api.request.rulemanager.InstallSpeakerCommandsRequest; +import org.openkilda.floodlight.api.request.rulemanager.ModifySpeakerCommandsRequest; public interface BatchCommandProcessor { void processBatchInstall(InstallSpeakerCommandsRequest request, String key); + void processBatchModify(ModifySpeakerCommandsRequest request, String key); + void processBatchDelete(DeleteSpeakerCommandsRequest request, String key); diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/BaseSpeakerCommandsRequest.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/BaseSpeakerCommandsRequest.java index 1a461d3f368..75dbb972cf7 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/BaseSpeakerCommandsRequest.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/BaseSpeakerCommandsRequest.java @@ -36,6 +36,8 @@ @JsonSubTypes({ @Type(value = InstallSpeakerCommandsRequest.class, name = "org.openkilda.floodlight.api.request.rulemanager.InstallSpeakerCommandsRequest"), + @Type(value = ModifySpeakerCommandsRequest.class, + name = "org.openkilda.floodlight.api.request.rulemanager.ModifySpeakerCommandsRequest"), @Type(value = DeleteSpeakerCommandsRequest.class, name = "org.openkilda.floodlight.api.request.rulemanager.DeleteSpeakerCommandsRequest") }) @@ -45,18 +47,27 @@ public abstract class BaseSpeakerCommandsRequest extends SpeakerRequest { @JsonProperty("command_data") protected Collection commands; + @JsonProperty("origin") + protected Origin origin; + public BaseSpeakerCommandsRequest(MessageContext messageContext, @NonNull SwitchId switchId, @NonNull UUID commandId, - Collection commands) { + Collection commands, + Origin origin) { super(messageContext, switchId, commandId); this.commands = commands; + this.origin = origin; } public Collection getCommands() { return commands; } + public Origin getOrigin() { + return origin; + } + public abstract void process(BatchCommandProcessor processor, String key); } diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/DeleteSpeakerCommandsRequest.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/DeleteSpeakerCommandsRequest.java index ce9125408e1..feeef43094f 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/DeleteSpeakerCommandsRequest.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/DeleteSpeakerCommandsRequest.java @@ -34,8 +34,9 @@ public class DeleteSpeakerCommandsRequest extends BaseSpeakerCommandsRequest { public DeleteSpeakerCommandsRequest(@JsonProperty("message_context") MessageContext messageContext, @JsonProperty("switch_id") @NonNull SwitchId switchId, @JsonProperty("command_id") @NonNull UUID commandId, - @JsonProperty("command_data") Collection commands) { - super(messageContext, switchId, commandId, commands); + @JsonProperty("command_data") Collection commands, + @JsonProperty("origin") Origin origin) { + super(messageContext, switchId, commandId, commands, origin); } public void process(BatchCommandProcessor processor, String key) { diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/FlowCommand.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/FlowCommand.java index 2a4295d13f0..827899fe1db 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/FlowCommand.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/FlowCommand.java @@ -39,6 +39,11 @@ public void buildInstall(OfEntityBatch builder, SwitchId switchId) { builder.addInstallFlow(data, switchId); } + @Override + public void buildModify(OfEntityBatch builder, SwitchId switchId) { + builder.addModifyFlow(data, switchId); + } + @Override public void buildDelete(OfEntityBatch builder, SwitchId switchId) { builder.addDeleteFlow(data, switchId); diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/GroupCommand.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/GroupCommand.java index 272e1e76350..6c32e75b469 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/GroupCommand.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/GroupCommand.java @@ -39,6 +39,11 @@ public void buildInstall(OfEntityBatch builder, SwitchId switchId) { builder.addInstallGroup(data, switchId); } + @Override + public void buildModify(OfEntityBatch builder, SwitchId switchId) { + builder.addModifyGroup(data, switchId); + } + @Override public void buildDelete(OfEntityBatch builder, SwitchId switchId) { builder.addDeleteGroup(data, switchId); diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/InstallSpeakerCommandsRequest.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/InstallSpeakerCommandsRequest.java index 3c2c38ab424..c0596ec4db6 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/InstallSpeakerCommandsRequest.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/InstallSpeakerCommandsRequest.java @@ -34,8 +34,9 @@ public class InstallSpeakerCommandsRequest extends BaseSpeakerCommandsRequest { public InstallSpeakerCommandsRequest(@JsonProperty("message_context") MessageContext messageContext, @JsonProperty("switch_id") @NonNull SwitchId switchId, @JsonProperty("command_id") @NonNull UUID commandId, - @JsonProperty("command_data") Collection commands) { - super(messageContext, switchId, commandId, commands); + @JsonProperty("command_data") Collection commands, + @JsonProperty("origin") Origin origin) { + super(messageContext, switchId, commandId, commands, origin); } public void process(BatchCommandProcessor processor, String key) { diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/MeterCommand.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/MeterCommand.java index b0077d4a4a6..21d7cd83329 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/MeterCommand.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/MeterCommand.java @@ -39,6 +39,11 @@ public void buildInstall(OfEntityBatch builder, SwitchId switchId) { builder.addInstallMeter(data, switchId); } + @Override + public void buildModify(OfEntityBatch builder, SwitchId switchId) { + builder.addModifyMeter(data, switchId); + } + @Override public void buildDelete(OfEntityBatch builder, SwitchId switchId) { builder.addDeleteMeter(data, switchId); diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/ModifySpeakerCommandsRequest.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/ModifySpeakerCommandsRequest.java new file mode 100644 index 00000000000..524e1dbfead --- /dev/null +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/ModifySpeakerCommandsRequest.java @@ -0,0 +1,45 @@ +/* Copyright 2021 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.floodlight.api.request.rulemanager; + +import org.openkilda.floodlight.api.BatchCommandProcessor; +import org.openkilda.messaging.MessageContext; +import org.openkilda.model.SwitchId; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Builder; +import lombok.NonNull; + +import java.util.Collection; +import java.util.UUID; + +public class ModifySpeakerCommandsRequest extends BaseSpeakerCommandsRequest { + + @Builder(toBuilder = true) + @JsonCreator + public ModifySpeakerCommandsRequest(@JsonProperty("message_context") MessageContext messageContext, + @JsonProperty("switch_id") @NonNull SwitchId switchId, + @JsonProperty("command_id") @NonNull UUID commandId, + @JsonProperty("command_data") Collection commands, + @JsonProperty("origin") Origin origin) { + super(messageContext, switchId, commandId, commands, origin); + } + + public void process(BatchCommandProcessor processor, String key) { + processor.processBatchModify(this, key); + } +} diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/OfCommand.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/OfCommand.java index 7ec32f6590b..e9b910beb6b 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/OfCommand.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/OfCommand.java @@ -35,5 +35,7 @@ public abstract class OfCommand { public abstract void buildInstall(OfEntityBatch builder, SwitchId switchId); + public abstract void buildModify(OfEntityBatch builder, SwitchId switchId); + public abstract void buildDelete(OfEntityBatch builder, SwitchId switchId); } diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/OfEntityBatch.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/OfEntityBatch.java index d28d1d9a245..6f6d5e18945 100644 --- a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/OfEntityBatch.java +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/OfEntityBatch.java @@ -24,13 +24,19 @@ public interface OfEntityBatch { void addInstallFlow(FlowSpeakerData data, SwitchId switchId); + void addModifyFlow(FlowSpeakerData data, SwitchId switchId); + void addDeleteFlow(FlowSpeakerData data, SwitchId switchId); void addInstallMeter(MeterSpeakerData data, SwitchId switchId); + void addModifyMeter(MeterSpeakerData data, SwitchId switchId); + void addDeleteMeter(MeterSpeakerData data, SwitchId switchId); void addInstallGroup(GroupSpeakerData data, SwitchId switchId); + void addModifyGroup(GroupSpeakerData data, SwitchId switchId); + void addDeleteGroup(GroupSpeakerData data, SwitchId switchId); } diff --git a/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/Origin.java b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/Origin.java new file mode 100644 index 00000000000..563721629be --- /dev/null +++ b/src-java/floodlight-service/floodlight-api/src/main/java/org/openkilda/floodlight/api/request/rulemanager/Origin.java @@ -0,0 +1,22 @@ +/* Copyright 2021 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.floodlight.api.request.rulemanager; + +public enum Origin { + + FLOW_HS, + SW_MANAGER +} diff --git a/src-java/floodlight-service/floodlight-modules/build.gradle b/src-java/floodlight-service/floodlight-modules/build.gradle index 0f733b10247..4e2454cb668 100644 --- a/src-java/floodlight-service/floodlight-modules/build.gradle +++ b/src-java/floodlight-service/floodlight-modules/build.gradle @@ -18,6 +18,7 @@ dependencies { implementation('org.projectfloodlight:openflowj') compileOnly('org.projectfloodlight:floodlight') testImplementation('org.projectfloodlight:floodlight') + testImplementation 'org.mockito:mockito-junit-jupiter' implementation('org.apache.kafka:kafka_2.11') { exclude group: 'org.slf4j', module: 'slf4j-log4j12' 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 f6da287b0ab..a422f4a700f 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 @@ -84,6 +84,10 @@ public String getSpeakerFlowTopic() { return formatTopicWithRegion(topics.getSpeakerFlowRegionTopic()); } + public String getSpeakerSwitchManagerTopic() { + return formatTopicWithRegion(topics.getSpeakerSwitchManagerRegionTopic()); + } + public String getSpeakerFlowPingTopic() { return formatTopicWithRegion(topics.getSpeakerFlowPingRegionTopic()); } @@ -128,6 +132,10 @@ public String getSpeakerFlowHsTopic() { return formatTopicWithRegion(topics.getFlowHsSpeakerRegionTopic()); } + public String getSpeakerSwitchManagerResponseTopic() { + return formatTopicWithRegion(topics.getSwitchManagerSpeakerRegionTopic()); + } + 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/command/rulemanager/OfBatchExecutor.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfBatchExecutor.java index 428e0b16133..1fe164c0027 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfBatchExecutor.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfBatchExecutor.java @@ -21,6 +21,7 @@ import static java.util.stream.Collectors.groupingBy; import org.openkilda.floodlight.KafkaChannel; +import org.openkilda.floodlight.api.request.rulemanager.Origin; import org.openkilda.floodlight.converter.rulemanager.OfFlowConverter; import org.openkilda.floodlight.converter.rulemanager.OfGroupConverter; import org.openkilda.floodlight.converter.rulemanager.OfMeterConverter; @@ -64,7 +65,7 @@ public class OfBatchExecutor { private final OfBatchHolder holder; private final Set switchFeatures; private final String kafkaKey; - + private final Origin origin; private boolean hasMeters; private boolean hasGroups; @@ -78,7 +79,7 @@ public class OfBatchExecutor { public OfBatchExecutor(IOFSwitch iofSwitch, KafkaUtilityService kafkaUtilityService, IKafkaProducerService kafkaProducerService, SessionService sessionService, MessageContext messageContext, OfBatchHolder holder, - Set switchFeatures, String kafkaKey) { + Set switchFeatures, String kafkaKey, Origin origin) { this.iofSwitch = iofSwitch; this.kafkaUtilityService = kafkaUtilityService; this.kafkaProducerService = kafkaProducerService; @@ -87,6 +88,7 @@ public OfBatchExecutor(IOFSwitch iofSwitch, KafkaUtilityService kafkaUtilityServ this.holder = holder; this.switchFeatures = switchFeatures; this.kafkaKey = kafkaKey; + this.origin = origin; } /** @@ -133,13 +135,19 @@ public void executeBatch() { } } else { log.error("Received error {}", ex.getMessage(), ex); + UUID uuid = holder.popAwaitingXid(message.getXid()); + holder.recordFailedUuid(uuid, ex.getMessage()); } })); } } CompletableFuture.allOf(requests.toArray(new CompletableFuture[0])) - .thenAccept(ignore -> checkOfResponses()); + .thenAccept(ignore -> checkOfResponses()) + .exceptionally(ignore -> { + checkOfResponses(); + return null; + }); } private void onSuccessfulOfMessage(OFMessage ofMessage) { @@ -286,8 +294,8 @@ private void verifyGroups() { List replies = groupStats.get(); List switchGroups = new ArrayList<>(); replies.forEach(reply -> switchGroups.addAll( - OfGroupConverter.INSTANCE.convertToGroupSpeakerData(reply))); - + OfGroupConverter.INSTANCE.convertToGroupSpeakerData(reply, + new SwitchId(iofSwitch.getId().getLong())))); for (GroupSpeakerData switchGroup : switchGroups) { GroupSpeakerData expectedGroup = holder.getByGroupId(switchGroup.getGroupId()); if (expectedGroup != null) { @@ -311,8 +319,19 @@ private void verifyGroups() { private void sendResponse() { KafkaChannel kafkaChannel = kafkaUtilityService.getKafkaChannel(); - log.debug("Send response to {} (key={})", kafkaChannel.getSpeakerFlowHsTopic(), kafkaKey); - kafkaProducerService.sendMessageAndTrack(kafkaChannel.getSpeakerFlowHsTopic(), - kafkaKey, holder.getResult()); + String topic = getTopic(kafkaChannel); + log.debug("Send response to {} (key={})", topic, kafkaKey); + kafkaProducerService.sendMessageAndTrack(topic, kafkaKey, holder.getResult()); + } + + private String getTopic(KafkaChannel kafkaChannel) { + switch (origin) { + case FLOW_HS: + return kafkaChannel.getSpeakerFlowHsTopic(); + case SW_MANAGER: + return kafkaChannel.getSpeakerSwitchManagerResponseTopic(); + default: + throw new IllegalStateException(format("Unknown message origin %s", origin)); + } } } diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfBatchHolder.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfBatchHolder.java index 7c6eaecdfcc..5289d9140a8 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfBatchHolder.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfBatchHolder.java @@ -170,6 +170,18 @@ public void addInstallFlow(FlowSpeakerData data, SwitchId switchId) { executionGraph.add(data.getUuid(), data.getDependsOn()); } + @Override + public void addModifyFlow(FlowSpeakerData data, SwitchId switchId) { + DatapathId dpId = DatapathId.of(switchId.toLong()); + OFFactory factory = iofSwitchService.getSwitch(dpId).getOFFactory(); + OFMessage message = OfFlowConverter.INSTANCE.convertModifyFlowCommand(data, factory); + xidMapping.put(message.getXid(), data.getUuid()); + BatchData batchData = BatchData.builder().flow(true).message(message).presenceBeVerified(true).build(); + commandMap.put(data.getUuid(), batchData); + flowsMap.put(data.getCookie(), data); + executionGraph.add(data.getUuid(), data.getDependsOn()); + } + @Override public void addDeleteFlow(FlowSpeakerData data, SwitchId switchId) { DatapathId dpId = DatapathId.of(switchId.toLong()); @@ -194,6 +206,18 @@ public void addInstallMeter(MeterSpeakerData data, SwitchId switchId) { executionGraph.add(data.getUuid(), data.getDependsOn()); } + @Override + public void addModifyMeter(MeterSpeakerData data, SwitchId switchId) { + DatapathId dpId = DatapathId.of(switchId.toLong()); + OFFactory factory = iofSwitchService.getSwitch(dpId).getOFFactory(); + OFMessage message = OfMeterConverter.INSTANCE.convertModifyMeterCommand(data, factory); + xidMapping.put(message.getXid(), data.getUuid()); + BatchData batchData = BatchData.builder().meter(true).message(message).presenceBeVerified(true).build(); + commandMap.put(data.getUuid(), batchData); + metersMap.put(data.getMeterId(), data); + executionGraph.add(data.getUuid(), data.getDependsOn()); + } + @Override public void addDeleteMeter(MeterSpeakerData data, SwitchId switchId) { DatapathId dpId = DatapathId.of(switchId.toLong()); @@ -218,6 +242,18 @@ public void addInstallGroup(GroupSpeakerData data, SwitchId switchId) { executionGraph.add(data.getUuid(), data.getDependsOn()); } + @Override + public void addModifyGroup(GroupSpeakerData data, SwitchId switchId) { + DatapathId dpId = DatapathId.of(switchId.toLong()); + OFFactory factory = iofSwitchService.getSwitch(dpId).getOFFactory(); + OFMessage message = OfGroupConverter.INSTANCE.convertModifyGroupCommand(data, factory); + xidMapping.put(message.getXid(), data.getUuid()); + BatchData batchData = BatchData.builder().group(true).message(message).presenceBeVerified(true).build(); + commandMap.put(data.getUuid(), batchData); + groupsMap.put(data.getGroupId(), data); + executionGraph.add(data.getUuid(), data.getDependsOn()); + } + @Override public void addDeleteGroup(GroupSpeakerData data, SwitchId switchId) { DatapathId dpId = DatapathId.of(switchId.toLong()); diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfSpeakerService.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfSpeakerService.java index 583b38d0706..ce56162c5cd 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfSpeakerService.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/command/rulemanager/OfSpeakerService.java @@ -18,6 +18,7 @@ import org.openkilda.floodlight.api.BatchCommandProcessor; import org.openkilda.floodlight.api.request.rulemanager.DeleteSpeakerCommandsRequest; import org.openkilda.floodlight.api.request.rulemanager.InstallSpeakerCommandsRequest; +import org.openkilda.floodlight.api.request.rulemanager.ModifySpeakerCommandsRequest; import org.openkilda.floodlight.api.request.rulemanager.OfCommand; import org.openkilda.floodlight.service.FeatureDetectorService; import org.openkilda.floodlight.service.kafka.IKafkaProducerService; @@ -26,11 +27,13 @@ import org.openkilda.model.SwitchId; import edu.umd.cs.findbugs.annotations.NonNull; +import lombok.extern.slf4j.Slf4j; import net.floodlightcontroller.core.IOFSwitch; import net.floodlightcontroller.core.internal.IOFSwitchService; import net.floodlightcontroller.core.module.FloodlightModuleContext; import org.projectfloodlight.openflow.types.DatapathId; +@Slf4j public class OfSpeakerService implements BatchCommandProcessor { private final IOFSwitchService iofSwitchService; private final SessionService sessionService; @@ -65,6 +68,31 @@ public void processBatchInstall(InstallSpeakerCommandsRequest request, String ke .holder(holder) .switchFeatures(featureDetectorService.detectSwitch(sw)) .kafkaKey(key) + .origin(request.getOrigin()) + .build(); + executor.executeBatch(); + } + + @Override + public void processBatchModify(ModifySpeakerCommandsRequest request, String key) { + SwitchId switchId = request.getSwitchId(); + DatapathId dpId = DatapathId.of(switchId.toLong()); + IOFSwitch sw = iofSwitchService.getSwitch(dpId); + OfBatchHolder holder = new OfBatchHolder(iofSwitchService, request.getMessageContext(), + request.getCommandId(), request.getSwitchId()); + for (OfCommand data : request.getCommands()) { + data.buildModify(holder, switchId); + } + OfBatchExecutor executor = OfBatchExecutor.builder() + .iofSwitch(sw) + .kafkaUtilityService(kafkaUtilityService) + .kafkaProducerService(kafkaProducerService) + .sessionService(sessionService) + .messageContext(request.getMessageContext()) + .holder(holder) + .switchFeatures(featureDetectorService.detectSwitch(sw)) + .kafkaKey(key) + .origin(request.getOrigin()) .build(); executor.executeBatch(); } @@ -88,6 +116,7 @@ public void processBatchDelete(DeleteSpeakerCommandsRequest request, String key) .holder(holder) .switchFeatures(featureDetectorService.detectSwitch(sw)) .kafkaKey(key) + .origin(request.getOrigin()) .build(); executor.executeBatch(); } diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfFlowConverter.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfFlowConverter.java index 975780542e5..12c9e7f1463 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfFlowConverter.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfFlowConverter.java @@ -81,15 +81,28 @@ public FlowSpeakerData convertToFlowSpeakerData(OFFlowStatsEntry entry, SwitchId * Convert flow speaker command data into OfFlowMod representation. */ public OFFlowMod convertInstallFlowCommand(FlowSpeakerData commandData, OFFactory ofFactory) { - return ofFactory.buildFlowAdd() - .setCookie(U64.of(commandData.getCookie().getValue())) + return setupBuilder(ofFactory.buildFlowAdd(), commandData, ofFactory) + .build(); + } + + /** + * Convert flow speaker command data into OfFlowMod representation for Flow modify. + */ + public OFFlowMod convertModifyFlowCommand(FlowSpeakerData commandData, OFFactory ofFactory) { + return setupBuilder(ofFactory.buildFlowModify(), commandData, ofFactory) + .build(); + } + + private OFFlowMod.Builder setupBuilder(OFFlowMod.Builder builder, + FlowSpeakerData commandData, OFFactory ofFactory) { + return builder.setCookie(U64.of(commandData.getCookie().getValue())) .setTableId(TableId.of(commandData.getTable().getTableId())) .setPriority(commandData.getPriority()) .setMatch(OfMatchConverter.INSTANCE.convertMatch(commandData.getMatch(), ofFactory)) .setInstructions( OfInstructionsConverter.INSTANCE.convertInstructions(commandData.getInstructions(), ofFactory)) - .setFlags(convertToOfFlags(commandData.getFlags())) - .build(); + .setFlags(convertToOfFlags(commandData.getFlags())); + } /** diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfGroupConverter.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfGroupConverter.java index 935f363deca..5f601baa4e1 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfGroupConverter.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfGroupConverter.java @@ -16,7 +16,9 @@ package org.openkilda.floodlight.converter.rulemanager; import org.openkilda.model.GroupId; +import org.openkilda.model.SwitchId; import org.openkilda.rulemanager.GroupSpeakerData; +import org.openkilda.rulemanager.OfVersion; import org.openkilda.rulemanager.action.Action; import org.openkilda.rulemanager.group.Bucket; import org.openkilda.rulemanager.group.GroupType; @@ -28,10 +30,10 @@ import org.projectfloodlight.openflow.protocol.OFBucket; import org.projectfloodlight.openflow.protocol.OFBucket.Builder; import org.projectfloodlight.openflow.protocol.OFFactory; -import org.projectfloodlight.openflow.protocol.OFGroupAdd; import org.projectfloodlight.openflow.protocol.OFGroupDelete; import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry; import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply; +import org.projectfloodlight.openflow.protocol.OFGroupMod; import org.projectfloodlight.openflow.protocol.OFGroupType; import org.projectfloodlight.openflow.protocol.action.OFAction; import org.projectfloodlight.openflow.types.OFGroup; @@ -50,16 +52,16 @@ public class OfGroupConverter { /** * Convert stats reply. */ - public List convertToGroupSpeakerData(OFGroupDescStatsReply statsReply) { + public List convertToGroupSpeakerData(OFGroupDescStatsReply statsReply, SwitchId switchId) { return statsReply.getEntries().stream() - .map(this::convertToGroupSpeakerData) + .map(entry -> convertToGroupSpeakerData(entry, switchId)) .collect(Collectors.toList()); } /** * Convert stats entry. */ - public GroupSpeakerData convertToGroupSpeakerData(OFGroupDescStatsEntry entry) { + public GroupSpeakerData convertToGroupSpeakerData(OFGroupDescStatsEntry entry, SwitchId switchId) { GroupId groupId = new GroupId(entry.getGroup().getGroupNumber()); GroupType type = fromOfGroupType(entry.getGroupType()); List buckets = new ArrayList<>(); @@ -68,6 +70,8 @@ public GroupSpeakerData convertToGroupSpeakerData(OFGroupDescStatsEntry entry) { buckets.add(fromOfBucket(bucket)); } return GroupSpeakerData.builder() + .switchId(switchId) + .ofVersion(OfVersion.of(entry.getVersion().name())) .groupId(groupId) .type(type) .buckets(buckets) @@ -77,15 +81,27 @@ public GroupSpeakerData convertToGroupSpeakerData(OFGroupDescStatsEntry entry) { /** * Convert group command data into OfGroupMod representation. */ - public OFGroupAdd convertInstallGroupCommand(GroupSpeakerData commandData, OFFactory ofFactory) { - List buckets = commandData.getBuckets().stream() - .map(x -> toOfBucket(ofFactory, x)).collect(Collectors.toList()); - return ofFactory.buildGroupAdd() - .setGroup(OFGroup.of((int) commandData.getGroupId().getValue())) - .setGroupType(toOfGroupType(commandData.getType())) - .setBuckets(buckets) + public OFGroupMod convertInstallGroupCommand(GroupSpeakerData commandData, OFFactory ofFactory) { + return setupBaseBuilder(ofFactory.buildGroupAdd(), commandData, ofFactory) .build(); + } + /** + * Convert group command data into OfGroupMod representation for group modify command. + */ + public OFGroupMod convertModifyGroupCommand(GroupSpeakerData commandData, OFFactory ofFactory) { + return setupBaseBuilder(ofFactory.buildGroupModify(), commandData, ofFactory) + .build(); + } + + private OFGroupMod.Builder setupBaseBuilder(OFGroupMod.Builder builder, GroupSpeakerData groupSpeakerData, + OFFactory ofFactory) { + List buckets = groupSpeakerData.getBuckets().stream() + .map(x -> toOfBucket(ofFactory, x)) + .collect(Collectors.toList()); + return builder.setGroup(OFGroup.of((int) groupSpeakerData.getGroupId().getValue())) + .setGroupType(toOfGroupType(groupSpeakerData.getType())) + .setBuckets(buckets); } /** diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfInstructionsConverter.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfInstructionsConverter.java index 2847997a07c..42f54c94672 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfInstructionsConverter.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfInstructionsConverter.java @@ -255,6 +255,7 @@ private OFAction convertAction(Action action, OFFactory ofFactory) { PortOutAction portOutAction = (PortOutAction) action; return ofFactory.actions().buildOutput() .setPort(convertPort(portOutAction.getPortNumber())) + .setMaxLen(0xFFFFFFFF) .build(); case POP_VLAN: return ofFactory.actions().popVlan(); @@ -284,6 +285,9 @@ private OFAction convertAction(Action action, OFFactory ofFactory) { .setIpv4Dst(IPv4Address.of(pushVxlanAction.getDstIpv4Address().getAddress())) .setUdpSrc(pushVxlanAction.getUdpSrc()) .setVni(pushVxlanAction.getVni()) + // Set to 0x01 indicating tunnel data is present + // (i.e. we are passing l2 and l3 headers in this action) + .setFlags((short) 0x01) .build(); } case METER: diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfMatchConverter.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfMatchConverter.java index c0628502355..45d14b4b2f3 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfMatchConverter.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfMatchConverter.java @@ -214,18 +214,18 @@ private void processFieldMatch(Builder builder, FieldMatch fieldMatch) { break; case ETH_SRC: if (fieldMatch.isMasked()) { - builder.setMasked(MatchField.ETH_SRC, MacAddress.of((int) fieldMatch.getValue()), + builder.setMasked(MatchField.ETH_SRC, MacAddress.of(fieldMatch.getValue()), MacAddress.of(fieldMatch.getMask().intValue())); } else { - builder.setExact(MatchField.ETH_SRC, MacAddress.of((int) fieldMatch.getValue())); + builder.setExact(MatchField.ETH_SRC, MacAddress.of(fieldMatch.getValue())); } break; case ETH_DST: if (fieldMatch.isMasked()) { - builder.setMasked(MatchField.ETH_DST, MacAddress.of((int) fieldMatch.getValue()), + builder.setMasked(MatchField.ETH_DST, MacAddress.of(fieldMatch.getValue()), MacAddress.of(fieldMatch.getMask().intValue())); } else { - builder.setExact(MatchField.ETH_DST, MacAddress.of((int) fieldMatch.getValue())); + builder.setExact(MatchField.ETH_DST, MacAddress.of(fieldMatch.getValue())); } break; case IN_PORT: diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfMeterConverter.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfMeterConverter.java index 8ec58d7f6d2..4cdfbd36a04 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfMeterConverter.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/converter/rulemanager/OfMeterConverter.java @@ -99,6 +99,21 @@ private Set fromOfMeterFlags(Set origin) { * @return mod */ public OFMeterMod convertInstallMeterCommand(MeterSpeakerData commandData, OFFactory ofFactory) { + return setupBaseMeterModBuilder(commandData, ofFactory) + .setCommand(OFMeterModCommand.ADD) + .build(); + } + + /** + * Convert Meter Modify Command. + */ + public OFMeterMod convertModifyMeterCommand(MeterSpeakerData commandData, OFFactory ofFactory) { + return setupBaseMeterModBuilder(commandData, ofFactory) + .setCommand(OFMeterModCommand.MODIFY) + .build(); + } + + private OFMeterMod.Builder setupBaseMeterModBuilder(MeterSpeakerData commandData, OFFactory ofFactory) { OFMeterMod.Builder builder = ofFactory.buildMeterMod(); builder.setMeterId(commandData.getMeterId().getValue()) .setFlags(toOfMeterFlags(commandData.getFlags())); @@ -115,7 +130,7 @@ public OFMeterMod convertInstallMeterCommand(MeterSpeakerData commandData, OFFac } else { builder.setMeters(bandDrops); } - return builder.build(); + return builder; } /** diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/kafka/KafkaMessageCollector.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/kafka/KafkaMessageCollector.java index 70a9560a832..12bd513e11e 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/kafka/KafkaMessageCollector.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/kafka/KafkaMessageCollector.java @@ -101,6 +101,7 @@ protected void launchTopics(KafkaMessageCollectorConfig consumerConfig, logger.info("Kafka Consumer: general executor threads = {}", consumerConfig.getGeneralExecutorCount()); launcher.launch(generalExecutor, new KafkaConsumerSetup(kafkaChannel.getSpeakerTopic())); launcher.launch(generalExecutor, new KafkaConsumerSetup(kafkaChannel.getSpeakerFlowTopic())); + launcher.launch(generalExecutor, new KafkaConsumerSetup(kafkaChannel.getSpeakerSwitchManagerTopic())); launcher.launch(generalExecutor, new KafkaConsumerSetup(kafkaChannel.getSpeakerFlowPingTopic())); ExecutorService discoCommandExecutor = buildExecutorWithNoQueue(consumerConfig.getDiscoExecutorCount()); diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/kafka/RecordHandler.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/kafka/RecordHandler.java index e5944659e1a..d9c51c042ab 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/kafka/RecordHandler.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/kafka/RecordHandler.java @@ -1306,7 +1306,7 @@ private void dumpRuleMangerGroupsRequest(SwitchId switchId, java.util.function.C .dumpGroups(DatapathId.of(switchId.toLong())); List groups = ofGroupDescStatsEntries.stream() - .map(OfGroupConverter.INSTANCE::convertToGroupSpeakerData) + .map(group -> OfGroupConverter.INSTANCE.convertToGroupSpeakerData(group, switchId)) .collect(Collectors.toList()); GroupDumpResponse response = GroupDumpResponse.builder() diff --git a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/service/zookeeper/ZooKeeperService.java b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/service/zookeeper/ZooKeeperService.java index 6d34ab5c836..7d3b32e877f 100644 --- a/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/service/zookeeper/ZooKeeperService.java +++ b/src-java/floodlight-service/floodlight-modules/src/main/java/org/openkilda/floodlight/service/zookeeper/ZooKeeperService.java @@ -43,8 +43,9 @@ public class ZooKeeperService implements IService, LifeCycleObserver { public static final String ZK_COMPONENT_NAME = "floodlight"; // management expected state consists of: // 1 producer - // 4 consumers for following topics: kilda.speaker, kilda.speaker.flow, kilda.speaker.flow.ping, kilda.speaker.disco - public static final int MANAGEMENT_EXPECTED_STATE = 5; + // 5 consumers for following topics: kilda.speaker, kilda.speaker.flow, kilda.speaker.flow.ping, + // kilda.speaker.disco, kilda.speaker.switch.manager + public static final int MANAGEMENT_EXPECTED_STATE = 6; // stats expected state consists of: // 1 producer // 1 consumers for topic kilda.speaker.disco diff --git a/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/command/rulemanager/OfBatchExecutorTest.java b/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/command/rulemanager/OfBatchExecutorTest.java new file mode 100644 index 00000000000..b5439fc6bd2 --- /dev/null +++ b/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/command/rulemanager/OfBatchExecutorTest.java @@ -0,0 +1,156 @@ +/* Copyright 2021 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.floodlight.command.rulemanager; + +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.when; + +import org.openkilda.floodlight.KafkaChannel; +import org.openkilda.floodlight.api.request.rulemanager.Origin; +import org.openkilda.floodlight.api.response.rulemanager.SpeakerCommandResponse; +import org.openkilda.floodlight.service.kafka.IKafkaProducerService; +import org.openkilda.floodlight.service.kafka.KafkaUtilityService; +import org.openkilda.floodlight.service.session.Session; +import org.openkilda.floodlight.service.session.SessionService; +import org.openkilda.messaging.MessageContext; +import org.openkilda.model.SwitchId; +import org.openkilda.model.cookie.Cookie; +import org.openkilda.rulemanager.FlowSpeakerData; +import org.openkilda.rulemanager.Instructions; +import org.openkilda.rulemanager.OfTable; + +import com.google.common.util.concurrent.SettableFuture; +import net.floodlightcontroller.core.IOFSwitch; +import net.floodlightcontroller.core.internal.IOFSwitchService; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.ArgumentCaptor; +import org.mockito.junit.MockitoJUnitRunner; +import org.projectfloodlight.openflow.protocol.OFFlowStatsReply; +import org.projectfloodlight.openflow.protocol.OFFlowStatsRequest; +import org.projectfloodlight.openflow.protocol.OFMessage; +import org.projectfloodlight.openflow.protocol.ver13.OFFactoryVer13; +import org.projectfloodlight.openflow.types.DatapathId; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.CompletableFuture; + +@RunWith(MockitoJUnitRunner.class) +public class OfBatchExecutorTest { + + private static final MessageContext MESSAGE_CONTEXT = new MessageContext("correlation-id"); + private static final SwitchId SWITCH_ID = new SwitchId("1"); + + IOFSwitch sw = mock(IOFSwitch.class); + KafkaUtilityService kafkaUtilityService = mock(KafkaUtilityService.class); + IKafkaProducerService kafkaProducerService = mock(IKafkaProducerService.class); + SessionService sessionService = mock(SessionService.class); + IOFSwitchService switchService = mock(IOFSwitchService.class); + + private final OfBatchHolder holder = new OfBatchHolder(switchService, MESSAGE_CONTEXT, + UUID.randomUUID(), SWITCH_ID); + private final OfBatchExecutor executor = OfBatchExecutor.builder() + .iofSwitch(sw) + .kafkaUtilityService(kafkaUtilityService) + .kafkaProducerService(kafkaProducerService) + .sessionService(sessionService) + .messageContext(MESSAGE_CONTEXT) + .holder(holder) + .switchFeatures(Collections.emptySet()) + .kafkaKey("kafka-key") + .origin(Origin.SW_MANAGER) + .build(); + + @Test + public void shouldSendSuccessResponse() { + when(switchService.getSwitch(DatapathId.of(SWITCH_ID.toLong()))).thenReturn(sw); + when(sw.getOFFactory()).thenReturn(new OFFactoryVer13()); + when(sw.getId()).thenReturn(DatapathId.of(SWITCH_ID.toLong())); + Session session = mock(Session.class); + when(sessionService.open(MESSAGE_CONTEXT, sw)).thenReturn(session); + when(session.write(any(OFMessage.class))).thenReturn(CompletableFuture.completedFuture(Optional.empty())); + OFFlowStatsReply reply = mock(OFFlowStatsReply.class); + when(reply.getEntries()).thenReturn(Collections.emptyList()); + SettableFuture> future = SettableFuture.create(); + future.set(Collections.singletonList(reply)); + when(sw.writeStatsRequest(any(OFFlowStatsRequest.class))).thenReturn(future); + KafkaChannel kafkaChannel = mock(KafkaChannel.class); + when(kafkaChannel.getSpeakerSwitchManagerResponseTopic()).thenReturn("kafka-topic"); + when(kafkaUtilityService.getKafkaChannel()).thenReturn(kafkaChannel); + + holder.addDeleteFlow(FlowSpeakerData.builder() + .switchId(SWITCH_ID) + .cookie(new Cookie(1)) + .priority(2) + .table(OfTable.INPUT) + .instructions(Instructions.builder().build()) + .build(), SWITCH_ID); + + executor.executeBatch(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SpeakerCommandResponse.class); + verify(kafkaProducerService).sendMessageAndTrack(any(String.class), any(String.class), + captor.capture()); + assertTrue(captor.getValue().isSuccess()); + + verifyNoMoreInteractions(kafkaProducerService); + } + + @Test + public void shouldSendFailedResponse() { + when(switchService.getSwitch(DatapathId.of(SWITCH_ID.toLong()))).thenReturn(sw); + when(sw.getOFFactory()).thenReturn(new OFFactoryVer13()); + when(sw.getId()).thenReturn(DatapathId.of(SWITCH_ID.toLong())); + Session session = mock(Session.class); + when(sessionService.open(MESSAGE_CONTEXT, sw)).thenReturn(session); + CompletableFuture> completableFuture = new CompletableFuture<>(); + completableFuture.completeExceptionally(new Exception("test exception")); + when(session.write(any(OFMessage.class))).thenReturn(completableFuture); + OFFlowStatsReply reply = mock(OFFlowStatsReply.class); + when(reply.getEntries()).thenReturn(Collections.emptyList()); + SettableFuture> future = SettableFuture.create(); + future.set(Collections.singletonList(reply)); + when(sw.writeStatsRequest(any(OFFlowStatsRequest.class))).thenReturn(future); + KafkaChannel kafkaChannel = mock(KafkaChannel.class); + when(kafkaChannel.getSpeakerSwitchManagerResponseTopic()).thenReturn("kafka-topic"); + when(kafkaUtilityService.getKafkaChannel()).thenReturn(kafkaChannel); + + holder.addDeleteFlow(FlowSpeakerData.builder() + .switchId(SWITCH_ID) + .cookie(new Cookie(1)) + .priority(2) + .table(OfTable.INPUT) + .instructions(Instructions.builder().build()) + .build(), SWITCH_ID); + + executor.executeBatch(); + + ArgumentCaptor captor = ArgumentCaptor.forClass(SpeakerCommandResponse.class); + verify(kafkaProducerService).sendMessageAndTrack(any(String.class), any(String.class), + captor.capture()); + assertFalse(captor.getValue().isSuccess()); + + verifyNoMoreInteractions(kafkaProducerService); + } +} diff --git a/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/OfInstructionsConverterTest.java b/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/OfInstructionsConverterTest.java index c2c182ed04c..af8d2fa860f 100644 --- a/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/OfInstructionsConverterTest.java +++ b/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/OfInstructionsConverterTest.java @@ -114,6 +114,7 @@ private static List buildActions(OFFactory factory) { actions.add(factory.actions().group(OFGroup.of(3))); actions.add(factory.actions().buildOutput() .setPort(OFPort.CONTROLLER) + .setMaxLen(0xFFFFFFFF) .build()); actions.add(factory.actions().popVlan()); actions.add(factory.actions().pushVlan(EthType.VLAN_FRAME)); @@ -126,6 +127,7 @@ private static List buildActions(OFFactory factory) { .setIpv4Dst(IPv4Address.of(44)) .setUdpSrc(55) .setVni(66) + .setFlags((short) 0x01) .build()); actions.add(factory.actions().buildKildaPushVxlanField() .setEthSrc(MacAddress.of(111)) diff --git a/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/rulemanager/OfGroupConverterTest.java b/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/rulemanager/OfGroupConverterTest.java index cd84e67f136..786d5b49b68 100644 --- a/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/rulemanager/OfGroupConverterTest.java +++ b/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/rulemanager/OfGroupConverterTest.java @@ -18,7 +18,9 @@ import static org.junit.Assert.assertEquals; import org.openkilda.model.GroupId; +import org.openkilda.model.SwitchId; import org.openkilda.rulemanager.GroupSpeakerData; +import org.openkilda.rulemanager.OfVersion; import org.openkilda.rulemanager.ProtoConstants.PortNumber; import org.openkilda.rulemanager.action.PortOutAction; import org.openkilda.rulemanager.group.Bucket; @@ -29,10 +31,10 @@ import com.google.common.collect.Sets; import org.junit.Test; import org.projectfloodlight.openflow.protocol.OFBucket; -import org.projectfloodlight.openflow.protocol.OFGroupAdd; import org.projectfloodlight.openflow.protocol.OFGroupDelete; import org.projectfloodlight.openflow.protocol.OFGroupDescStatsEntry; import org.projectfloodlight.openflow.protocol.OFGroupDescStatsReply.Builder; +import org.projectfloodlight.openflow.protocol.OFGroupMod; import org.projectfloodlight.openflow.protocol.OFGroupType; import org.projectfloodlight.openflow.protocol.action.OFAction; import org.projectfloodlight.openflow.protocol.ver13.OFFactoryVer13; @@ -46,11 +48,12 @@ public class OfGroupConverterTest { + private static final SwitchId SWITCH_ID = new SwitchId(1); private static final int GROUP_ID = 12; private List getActions(OFFactoryVer13 factory, int portNumber) { List list = new ArrayList<>(); - list.add(factory.actions().buildOutput().setPort(OFPort.of(portNumber)).build()); + list.add(factory.actions().buildOutput().setPort(OFPort.of(portNumber)).setMaxLen(0xFFFFFFFF).build()); return list; } @@ -82,9 +85,11 @@ public void testConvertToGroupSpeakerData() { builder.setEntries(entries); List groupSpeakerDataList = OfGroupConverter.INSTANCE.convertToGroupSpeakerData( - builder.build()); + builder.build(), SWITCH_ID); assertEquals(1, groupSpeakerDataList.size()); GroupSpeakerData groupSpeakerData = groupSpeakerDataList.get(0); + assertEquals(SWITCH_ID, groupSpeakerData.getSwitchId()); + assertEquals(OfVersion.OF_13, groupSpeakerData.getOfVersion()); assertEquals(new GroupId(GROUP_ID), groupSpeakerData.getGroupId()); assertEquals(GroupType.ALL, groupSpeakerData.getType()); List buckets = groupSpeakerData.getBuckets(); @@ -114,11 +119,11 @@ public void testConvertInstallGroupCommand() { .build(); OFFactoryVer13 factory = new OFFactoryVer13(); - OFGroupAdd ofGroupAdd = OfGroupConverter.INSTANCE.convertInstallGroupCommand(groupSpeakerData, factory); + OFGroupMod ofGroupMod = OfGroupConverter.INSTANCE.convertInstallGroupCommand(groupSpeakerData, factory); - assertEquals(OFGroup.of(GROUP_ID), ofGroupAdd.getGroup()); - assertEquals(OFGroupType.ALL, ofGroupAdd.getGroupType()); - assertEquals(2, ofGroupAdd.getBuckets().size()); + assertEquals(OFGroup.of(GROUP_ID), ofGroupMod.getGroup()); + assertEquals(OFGroupType.ALL, ofGroupMod.getGroupType()); + assertEquals(2, ofGroupMod.getBuckets().size()); List expectedBuckets = new ArrayList<>(); expectedBuckets.add(factory.buildBucket().setWatchPort(OFPort.ANY) @@ -130,7 +135,7 @@ public void testConvertInstallGroupCommand() { .setWatchGroup(OFGroup.ALL) .setActions(getActions(factory, 1)) .build()); - assertEquals(expectedBuckets, ofGroupAdd.getBuckets()); + assertEquals(expectedBuckets, ofGroupMod.getBuckets()); } @Test diff --git a/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/rulemanager/OfMatchConverterTest.java b/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/rulemanager/OfMatchConverterTest.java index 5ba92c92b9f..526cbbbda50 100644 --- a/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/rulemanager/OfMatchConverterTest.java +++ b/src-java/floodlight-service/floodlight-modules/src/test/java/org/openkilda/floodlight/converter/rulemanager/OfMatchConverterTest.java @@ -103,7 +103,7 @@ public void testConvertMatchExact() { OFFactoryVer13 factory = new OFFactoryVer13(); Set matchSet = new HashSet<>(); matchSet.add(FieldMatch.builder().field(Field.ETH_TYPE).value(0x0800L).build()); - matchSet.add(FieldMatch.builder().field(Field.ETH_SRC).value(1).build()); + matchSet.add(FieldMatch.builder().field(Field.ETH_SRC).value(167000408061L).build()); matchSet.add(FieldMatch.builder().field(Field.ETH_DST).value(2).build()); matchSet.add(FieldMatch.builder().field(Field.IP_PROTO).value(17).build()); matchSet.add(FieldMatch.builder().field(Field.UDP_SRC).value(11).build()); @@ -119,7 +119,7 @@ public void testConvertMatchExact() { assertEquals(0x800, ethType.getValue()); MacAddress ethSrc = match.get(MatchField.ETH_SRC); - assertEquals(MacAddress.of(1), ethSrc); + assertEquals(MacAddress.of(167000408061L), ethSrc); MacAddress ethDst = match.get(MatchField.ETH_DST); assertEquals(MacAddress.of(2), ethDst); 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 d0760ceba0b..4e3c3d3d6b1 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 @@ -17,6 +17,7 @@ public final class ComponentType { public static final String KILDA_FLOW_HS_REPLY_BOLT = "KILDA_FLOW_HS_REPLY_BOLT"; + public static final String KILDA_SWITCH_MANAGER_RESPONSE_REPLY_BOLT = "KILDA_SWITCH_MANAGER_RESPONSE_REPLY_BOLT"; public static final String KILDA_PING_REPLY_BOLT = "KILDA_PING_REPLY_BOLT"; public static final String NORTHBOUND_REPLY_BOLT = "NORTHBOUND_REPLY_BOLT"; public static final String KILDA_SWITCH_MANAGER_REPLY_BOLT = "KILDA_SWITCH_MANAGER_REPLY_BOLT"; @@ -28,12 +29,15 @@ public final class ComponentType { public static final String KILDA_TOPO_DISCO_BOLT = "KILDA_TOPO_DISCO_BOLT"; public static final String SPEAKER_FLOW_REQUEST_BOLT = "KILDA_FLOW_REQUEST_BOLT"; + public static final String SPEAKER_SWITCH_MANAGER_REQUEST_BOLT = "KILDA_SWITCH_MANAGER_REQUEST_BOLT"; public static final String SPEAKER_REQUEST_BOLT = "SPEAKER_REQUEST_BOLT"; public static final String SPEAKER_DISCO_REQUEST_BOLT = "SPEAKER_DISCO_REQUEST_BOLT"; public static final String SPEAKER_KAFKA_SPOUT = "ROUTER_SPEAKER_KAFKA_SPOUT"; public static final String KILDA_FLOW_HS_KAFKA_SPOUT = "KILDA_FLOW_HS_KAFKA_SPOUT"; + public static final String KILDA_SWITCH_MANAGER_REQUEST_KAFKA_SPOUT = "KILDA_SWITCH_MANAGER_REQUEST_KAFKA_SPOUT"; public static final String SPEAKER_FLOW_HS_KAFKA_SPOUT = "ROUTER_SPEAKER_FLOW_HS_KAFKA_SPOUT"; + public static final String SPEAKER_SWITCH_MANAGER_KAFKA_SPOUT = "ROUTER_SPEAKER_SW_MANAGER_KAFKA_SPOUT"; public static final String SPEAKER_PING_KAFKA_SPOUT = "SPEAKER_PING_KAFKA_SPOUT"; public static final String KILDA_PING_KAFKA_SPOUT = "KILDA_PING_KAFKA_SPOUT"; public static final String KILDA_STATS_KAFKA_SPOUT = "KILDA_STATS_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 c6e9e1691e0..65d20d50847 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 @@ -216,6 +216,22 @@ private void speakerToConnectedDevices(TopologyBuilder topology, TopologyOutput } private void speakerToSwitchManager(TopologyBuilder topology, TopologyOutput output) { + declareKafkaSpoutForAbstractMessage(topology, + kafkaTopics.getSpeakerSwitchManagerTopic(), ComponentType.SPEAKER_SWITCH_MANAGER_KAFKA_SPOUT); + declareControllerToSpeakerProxy( + topology, kafkaTopics.getSpeakerSwitchManagerRegionTopic(), + ComponentType.SPEAKER_SWITCH_MANAGER_KAFKA_SPOUT, ComponentType.SPEAKER_SWITCH_MANAGER_REQUEST_BOLT, + output.getKafkaHsOutput()); + + declareKafkaSpoutForAbstractMessage(topology, + makeRegionTopics(kafkaTopics.getSwitchManagerSpeakerRegionTopic()), + ComponentType.KILDA_SWITCH_MANAGER_REQUEST_KAFKA_SPOUT); + declareSpeakerToControllerProxy( + topology, kafkaTopics.getSwitchManagerSpeakerTopic(), + ComponentType.KILDA_SWITCH_MANAGER_REQUEST_KAFKA_SPOUT, + ComponentType.KILDA_SWITCH_MANAGER_RESPONSE_REPLY_BOLT, + output.getKafkaHsOutput()); + declareSpeakerToControllerProxy( topology, kafkaTopics.getTopoSwitchManagerRegionTopic(), kafkaTopics.getTopoSwitchManagerTopic(), ComponentType.KILDA_SWITCH_MANAGER_KAFKA_SPOUT, ComponentType.KILDA_SWITCH_MANAGER_REPLY_BOLT, diff --git a/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/BaseFlowRuleRemovalAction.java b/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/BaseFlowRuleRemovalAction.java index 18adc0090be..2cf37860f01 100644 --- a/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/BaseFlowRuleRemovalAction.java +++ b/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/BaseFlowRuleRemovalAction.java @@ -109,53 +109,91 @@ protected boolean removeReverseCustomerPortSharedCatchRule(RequestedFlow oldFlow } protected boolean removeSharedServer42InputRule( - FlowEndpoint oldEndpoint, FlowEndpoint newEndpoint, boolean server42Rtt, boolean becameSingleSwitch) { - return server42Rtt && (!oldEndpoint.isSwitchPortEquals(newEndpoint) || becameSingleSwitch) - && findFlowIdsForMultiSwitchFlowsByEndpointWithMultiTableSupport( + FlowEndpoint oldEndpoint, FlowEndpoint newEndpoint, FlowEndpoint oppositeNewEndpoint, + boolean server42Rtt, boolean becameSingleSwitch) { + if (!server42Rtt) { + return false; // server42 is off so nothing to delete. + } + if (oldEndpoint.isSwitchPortEquals(newEndpoint) && !becameSingleSwitch) { + return false; // new endpoint still need server42 input rule + } + + if (oldEndpoint.isSwitchPortEquals(oppositeNewEndpoint) && !becameSingleSwitch) { + return false; // opposite new endpoint will use existing server42 input rule + } + + // Current flow doesn't use server42 input rule anymore, but maybe some other flow still need this rule. + return findFlowIdsForMultiSwitchFlowsByEndpointWithMultiTableSupport( oldEndpoint.getSwitchId(), oldEndpoint.getPortNumber()).isEmpty(); } - protected boolean removeSharedLldpRule(String flowId, FlowEndpoint oldEndpoint, FlowEndpoint newEndpoint) { - boolean lldpWasSwitchedOff = oldEndpoint.isTrackLldpConnectedDevices() - && !newEndpoint.isTrackLldpConnectedDevices(); - boolean lldpPortWasChanged = !oldEndpoint.isSwitchPortEquals(newEndpoint) - && oldEndpoint.isTrackLldpConnectedDevices(); + protected boolean removeSharedLldpRule( + String flowId, FlowEndpoint oldEndpoint, FlowEndpoint newEndpoint, FlowEndpoint oppositeNewEndpoint) { + if (!oldEndpoint.isTrackLldpConnectedDevices()) { + return false; // LLDP is off on endpoint so nothing to delete. + } + if (oldEndpoint.isSwitchPortEquals(newEndpoint) && newEndpoint.isTrackLldpConnectedDevices()) { + return false; // LLDP still on and endpoint still same. We need LLDP rule. + } + if (oldEndpoint.isSwitchPortEquals(oppositeNewEndpoint) && oppositeNewEndpoint.isTrackLldpConnectedDevices()) { + return false; // Opposite endpoint will use LLDP rule of oldEndpoint. + } - return (lldpWasSwitchedOff || lldpPortWasChanged) && isFlowTheLastUserOfSharedLldpPortRule( - flowId, oldEndpoint.getSwitchId(), oldEndpoint.getPortNumber()); + // Current flow doesn't use shared LLDP rule anymore, but maybe some other flow still need this rule. + return isFlowTheLastUserOfSharedLldpPortRule(flowId, oldEndpoint.getSwitchId(), oldEndpoint.getPortNumber()); } - protected boolean removeSharedArpRule(String flowId, FlowEndpoint oldEndpoint, FlowEndpoint newEndpoint) { - boolean arpWasSwitchedOff = oldEndpoint.isTrackArpConnectedDevices() - && !newEndpoint.isTrackArpConnectedDevices(); - boolean arpPortWasChanged = !oldEndpoint.isSwitchPortEquals(newEndpoint) - && oldEndpoint.isTrackArpConnectedDevices(); + protected boolean removeSharedArpRule( + String flowId, FlowEndpoint oldEndpoint, FlowEndpoint newEndpoint, FlowEndpoint oppositeNewEndpoint) { + if (!oldEndpoint.isTrackArpConnectedDevices()) { + return false; // ARP is off on endpoint so nothing to delete. + } + if (oldEndpoint.isSwitchPortEquals(newEndpoint) && newEndpoint.isTrackArpConnectedDevices()) { + return false; // ARP still on and endpoint still same. We need LLDP rule. + } + if (oldEndpoint.isSwitchPortEquals(oppositeNewEndpoint) && oppositeNewEndpoint.isTrackArpConnectedDevices()) { + return false; // Opposite endpoint will use ARP rule of oldEndpoint. + } - return (arpWasSwitchedOff || arpPortWasChanged) && isFlowTheLastUserOfSharedArpPortRule( - flowId, oldEndpoint.getSwitchId(), oldEndpoint.getPortNumber()); + // Current flow doesn't use shared ARP rule anymore, but maybe some other flow still need this rule. + return isFlowTheLastUserOfSharedArpPortRule(flowId, oldEndpoint.getSwitchId(), oldEndpoint.getPortNumber()); } - protected boolean removeOuterVlanMatchSharedRule(String flowId, FlowEndpoint current, FlowEndpoint goal) { - if (current.isSwitchPortEquals(goal) - && current.getOuterVlanId() == goal.getOuterVlanId()) { - return false; + protected boolean removeOuterVlanMatchSharedRule( + String flowId, FlowEndpoint oldEndpoint, FlowEndpoint newEndpoint, FlowEndpoint oppositeNewEndpoint) { + if (oldEndpoint.isSwitchPortOuterVlanEquals(newEndpoint)) { + return false; // new endpoint still need shared rule } - return findOuterVlanMatchSharedRuleUsage(current).stream() + + if (oldEndpoint.isSwitchPortOuterVlanEquals(oppositeNewEndpoint)) { + return false; // opposite new endpoint will use existing shared rule + } + + // Current flow doesn't use shared rule anymore, but maybe some other flow still need this rule. + return findOuterVlanMatchSharedRuleUsage(oldEndpoint).stream() .allMatch(entry -> flowId.equals(entry.getFlowId())); } protected boolean removeServer42OuterVlanMatchSharedRule( - RequestedFlow currentFlow, FlowEndpoint current, FlowEndpoint goal, + RequestedFlow oldFlow, FlowEndpoint oldEndpoint, FlowEndpoint newEndpoint, FlowEndpoint oppositeNewEndpoint, boolean server42FlowRtt, boolean flowBecameSingleSwitch) { - if (currentFlow.isOneSwitchFlow() || !server42FlowRtt) { - return false; + if (oldFlow.isOneSwitchFlow() || !server42FlowRtt) { + return false; // flow endpoint has no server42 rules so nothing to delete. + } + + if (oldEndpoint.getSwitchId().equals(newEndpoint.getSwitchId()) + && oldEndpoint.getOuterVlanId() == newEndpoint.getOuterVlanId() && !flowBecameSingleSwitch) { + return false; // new endpoint still need shared server42 rule + } + + if (oldEndpoint.getSwitchId().equals(oppositeNewEndpoint.getSwitchId()) + && oldEndpoint.getOuterVlanId() == oppositeNewEndpoint.getOuterVlanId() && !flowBecameSingleSwitch) { + return false; // opposite new endpoint will use existing shared server42 rule } - boolean endpointChanged = !current.getSwitchId().equals(goal.getSwitchId()) - || current.getOuterVlanId() != goal.getOuterVlanId(); - return (endpointChanged || flowBecameSingleSwitch) - && findServer42OuterVlanMatchSharedRuleUsage(current).stream() - .allMatch(currentFlow.getFlowId()::equals); + // Current flow doesn't use shared server42 rule anymore, but maybe some other flow still need this rule. + return findServer42OuterVlanMatchSharedRuleUsage(oldEndpoint).stream() + .allMatch(oldFlow.getFlowId()::equals); } protected SpeakerRequestBuildContext buildSpeakerContextForRemovalIngressAndShared( @@ -176,16 +214,17 @@ protected SpeakerRequestBuildContext buildSpeakerContextForRemovalIngressAndShar PathContext forwardPathContext = PathContext.builder() .removeCustomerPortRule(removeForwardCustomerPortSharedCatchRule(oldFlow, newFlow)) - .removeCustomerPortLldpRule(removeSharedLldpRule(oldFlow.getFlowId(), oldIngress, newIngress)) - .removeCustomerPortArpRule(removeSharedArpRule(oldFlow.getFlowId(), oldIngress, newIngress)) + .removeCustomerPortLldpRule(removeSharedLldpRule( + oldFlow.getFlowId(), oldIngress, newIngress, newEgress)) + .removeCustomerPortArpRule(removeSharedArpRule(oldFlow.getFlowId(), oldIngress, newIngress, newEgress)) .removeOuterVlanMatchSharedRule( - removeOuterVlanMatchSharedRule(oldFlow.getFlowId(), oldIngress, newIngress)) + removeOuterVlanMatchSharedRule(oldFlow.getFlowId(), oldIngress, newIngress, newEgress)) .removeServer42InputRule(removeSharedServer42InputRule( - oldIngress, newIngress, srcServer42FlowRtt, becameSingleSwitch)) + oldIngress, newIngress, newEgress, srcServer42FlowRtt, becameSingleSwitch)) .removeServer42IngressRule(srcServer42FlowRtt) .updateMeter(removeMeters) .removeServer42OuterVlanMatchSharedRule(removeServer42OuterVlanMatchSharedRule( - oldFlow, oldIngress, newIngress, srcServer42FlowRtt, becameSingleSwitch)) + oldFlow, oldIngress, newIngress, newEgress, srcServer42FlowRtt, becameSingleSwitch)) .server42Port(oldSrcSwitchProperties.getServer42Port()) .server42MacAddress(oldSrcSwitchProperties.getServer42MacAddress()) .build(); @@ -194,16 +233,16 @@ protected SpeakerRequestBuildContext buildSpeakerContextForRemovalIngressAndShar PathContext reversePathContext = PathContext.builder() .removeCustomerPortRule(removeReverseCustomerPortSharedCatchRule(oldFlow, newFlow)) - .removeCustomerPortLldpRule(removeSharedLldpRule(oldFlow.getFlowId(), oldEgress, newEgress)) - .removeCustomerPortArpRule(removeSharedArpRule(oldFlow.getFlowId(), oldEgress, newEgress)) + .removeCustomerPortLldpRule(removeSharedLldpRule(oldFlow.getFlowId(), oldEgress, newEgress, newIngress)) + .removeCustomerPortArpRule(removeSharedArpRule(oldFlow.getFlowId(), oldEgress, newEgress, newIngress)) .removeOuterVlanMatchSharedRule( - removeOuterVlanMatchSharedRule(oldFlow.getFlowId(), oldEgress, newEgress)) + removeOuterVlanMatchSharedRule(oldFlow.getFlowId(), oldEgress, newEgress, newIngress)) .removeServer42InputRule(removeSharedServer42InputRule( - oldEgress, newEgress, dstServer42FlowRtt, becameSingleSwitch)) + oldEgress, newEgress, newIngress, dstServer42FlowRtt, becameSingleSwitch)) .removeServer42IngressRule(dstServer42FlowRtt) .updateMeter(removeMeters) .removeServer42OuterVlanMatchSharedRule(removeServer42OuterVlanMatchSharedRule( - oldFlow, oldEgress, newEgress, dstServer42FlowRtt, becameSingleSwitch)) + oldFlow, oldEgress, newEgress, newIngress, dstServer42FlowRtt, becameSingleSwitch)) .server42Port(oldDstSwitchProperties.getServer42Port()) .server42MacAddress(oldDstSwitchProperties.getServer42MacAddress()) .build(); diff --git a/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/YFlowRuleManagerProcessingAction.java b/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/YFlowRuleManagerProcessingAction.java index e06b2b7abc7..a934008c233 100644 --- a/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/YFlowRuleManagerProcessingAction.java +++ b/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/YFlowRuleManagerProcessingAction.java @@ -24,6 +24,7 @@ import org.openkilda.floodlight.api.request.rulemanager.InstallSpeakerCommandsRequest; import org.openkilda.floodlight.api.request.rulemanager.MeterCommand; import org.openkilda.floodlight.api.request.rulemanager.OfCommand; +import org.openkilda.floodlight.api.request.rulemanager.Origin; import org.openkilda.messaging.MessageContext; import org.openkilda.model.Flow; import org.openkilda.model.FlowPath; @@ -74,7 +75,8 @@ protected Collection buildYFlowInstallCommands(YF List dataList = entry.getValue(); UUID commandId = commandIdGenerator.generate(); MessageContext messageContext = new MessageContext(commandId.toString(), context.getCorrelationId()); - return new InstallSpeakerCommandsRequest(messageContext, switchId, commandId, mapToOfCommands(dataList)); + return new InstallSpeakerCommandsRequest(messageContext, switchId, commandId, mapToOfCommands(dataList), + Origin.FLOW_HS); }).collect(Collectors.toList()); } @@ -86,7 +88,8 @@ protected Collection buildYFlowDeleteCommands(YFlo List dataList = reverseDependenciesForDeletion(entry.getValue()); UUID commandId = commandIdGenerator.generate(); MessageContext messageContext = new MessageContext(commandId.toString(), context.getCorrelationId()); - return new DeleteSpeakerCommandsRequest(messageContext, switchId, commandId, mapToOfCommands(dataList)); + return new DeleteSpeakerCommandsRequest(messageContext, switchId, commandId, mapToOfCommands(dataList), + Origin.FLOW_HS); }).collect(Collectors.toList()); } diff --git a/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/pathswap/actions/UpdateYFlowRulesAction.java b/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/pathswap/actions/UpdateYFlowRulesAction.java index 77b7976c2e6..c5dcb16e52b 100644 --- a/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/pathswap/actions/UpdateYFlowRulesAction.java +++ b/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/pathswap/actions/UpdateYFlowRulesAction.java @@ -23,6 +23,7 @@ import org.openkilda.floodlight.api.request.rulemanager.FlowCommand; import org.openkilda.floodlight.api.request.rulemanager.InstallSpeakerCommandsRequest; import org.openkilda.floodlight.api.request.rulemanager.OfCommand; +import org.openkilda.floodlight.api.request.rulemanager.Origin; import org.openkilda.messaging.MessageContext; import org.openkilda.messaging.error.ErrorType; import org.openkilda.model.Flow; @@ -102,7 +103,7 @@ protected InstallSpeakerCommandsRequest buildYFlowInstallRequest(SwitchId switch UUID commandId = commandIdGenerator.generate(); MessageContext messageContext = new MessageContext(commandId.toString(), context.getCorrelationId()); - return new InstallSpeakerCommandsRequest(messageContext, switchId, commandId, ofCommands); + return new InstallSpeakerCommandsRequest(messageContext, switchId, commandId, ofCommands, Origin.FLOW_HS); } protected DeleteSpeakerCommandsRequest buildYFlowDeleteRequest(SwitchId switchId, PathId pathId, @@ -111,7 +112,7 @@ protected DeleteSpeakerCommandsRequest buildYFlowDeleteRequest(SwitchId switchId UUID commandId = commandIdGenerator.generate(); MessageContext messageContext = new MessageContext(commandId.toString(), context.getCorrelationId()); - return new DeleteSpeakerCommandsRequest(messageContext, switchId, commandId, ofCommands); + return new DeleteSpeakerCommandsRequest(messageContext, switchId, commandId, ofCommands, Origin.FLOW_HS); } private List buildYFlowOfCommands(SwitchId switchId, PathId pathId) { diff --git a/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/update/actions/CompleteFlowPathRemovalAction.java b/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/update/actions/CompleteFlowPathRemovalAction.java index afa5b107e98..8e782223d5f 100644 --- a/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/update/actions/CompleteFlowPathRemovalAction.java +++ b/src-java/flowhs-topology/flowhs-storm-topology/src/main/java/org/openkilda/wfm/topology/flowhs/fsm/update/actions/CompleteFlowPathRemovalAction.java @@ -74,6 +74,7 @@ private void removeOldPrimaryFlowPaths(Flow originalFlow, FlowUpdateFsm stateMac updateIslsForFlowPath(removedPaths.getReverse()); } if (removedPaths != null) { + restoreFlowEndpoints(removedPaths, originalFlow); saveRemovalActionWithDumpToHistory(stateMachine, originalFlow, removedPaths); } } @@ -106,11 +107,20 @@ private void removeOldProtectedFlowPaths(Flow originalFlow, FlowUpdateFsm stateM updateIslsForFlowPath(removedPaths.getReverse()); } if (removedPaths != null) { + restoreFlowEndpoints(removedPaths, originalFlow); saveRemovalActionWithDumpToHistory(stateMachine, originalFlow, removedPaths); } } } + private void restoreFlowEndpoints(FlowPathPair flowPathPair, Flow originalFlow) { + Flow flow = flowPathPair.getForward().getFlow(); + flow.setSrcSwitch(originalFlow.getSrcSwitch()); + flow.setSrcPort(originalFlow.getSrcPort()); + flow.setDestSwitch(originalFlow.getDestSwitch()); + flow.setDestPort(originalFlow.getDestPort()); + } + private void removeRejectedFlowPaths(Flow flow, FlowUpdateFsm stateMachine) { stateMachine.getRejectedPaths().stream() .forEach(pathId -> diff --git a/src-java/flowhs-topology/flowhs-storm-topology/src/test/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/BaseFlowRuleRemovalActionTest.java b/src-java/flowhs-topology/flowhs-storm-topology/src/test/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/BaseFlowRuleRemovalActionTest.java index ef2914971ae..368c96ccf8c 100644 --- a/src-java/flowhs-topology/flowhs-storm-topology/src/test/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/BaseFlowRuleRemovalActionTest.java +++ b/src-java/flowhs-topology/flowhs-storm-topology/src/test/java/org/openkilda/wfm/topology/flowhs/fsm/common/actions/BaseFlowRuleRemovalActionTest.java @@ -115,82 +115,135 @@ public void buildSpeakerContextForRemovalIngressAndSharedTest() { public void turnOffArpAndLldpTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, true, true); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, false, false); - assertTrue(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); - assertTrue(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3, 0, 0, false, false); + assertTrue(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertTrue(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); } @Test public void turnOnArpAndLldpTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, false, false); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, true, true); - assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); - assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3, 0, 0, false, false); + assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); } @Test public void doNotChangeArpAndLldpTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, false, true); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, false, true); - assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); - assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3, 0, 0, false, false); + assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); } @Test public void changePortForOnArpAndLldpTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, true, true); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_2, 0, 0, true, true); - assertTrue(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); - assertTrue(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3, 0, 0, false, false); + assertTrue(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertTrue(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); } @Test public void changePortForOffArpAndLldpTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, false, false); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_2, 0, 0, false, false); - assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); - assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3, 0, 0, false, false); + assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); } @Test public void changeSwitchForOnArpAndLldpTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, true, true); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_1, 0, 0, true, true); - assertTrue(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); - assertTrue(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3, 0, 0, false, false); + assertTrue(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertTrue(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); } @Test public void changeSwitchForOffArpAndLldpTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, false, false); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_1, 0, 0, false, false); - assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); - assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3, 0, 0, false, false); + assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + } + + @Test + public void swapEndpointOnArpAndLldpTest() { + FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, true, true); + FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_2, 0, 0, true, true); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, true, true); + assertFalse(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertFalse(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + } + + @Test + public void swapEndpointTurnOffArpAndLldpTest() { + FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, true, true); + FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_2, 0, 0, true, true); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, 0, 0, false, false); + assertTrue(testClass.removeSharedLldpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); + assertTrue(testClass.removeSharedArpRule(FLOW_ID_1, oldEndpoint, newEndpoint, oppositeNewEndpoint)); } @Test public void sameEndpointRemoveSharedServer42InputRuleTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, true, false)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, false, false)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, newEndpoint, oppositeNewEndpoint, true, false)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, newEndpoint, oppositeNewEndpoint, false, false)); } @Test public void changeSwitchRemoveSharedServer42InputRuleTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_1); - assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, true, false)); - assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, true, true)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, false, false)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3); + assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, oppositeNewEndpoint, true, false)); + assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, oppositeNewEndpoint, true, true)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, newEndpoint, oppositeNewEndpoint, false, false)); } @Test public void changePortRemoveSharedServer42InputRuleTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_2); - assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, true, false)); - assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, true, true)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, false, false)); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3); + assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, oppositeNewEndpoint, true, false)); + assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, oppositeNewEndpoint, true, true)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, newEndpoint, oppositeNewEndpoint, false, false)); + } + + @Test + public void swapEndpointMultiSwitchRemoveSharedServer42InputRuleTest() { + FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); + FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_2); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, newEndpoint, oppositeNewEndpoint, true, false)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, newEndpoint, oppositeNewEndpoint, false, false)); + } + + @Test + public void swapEndpointSingleSwitchRemoveSharedServer42InputRuleTest() { + FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); + FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_2); + FlowEndpoint oppositeNewEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); + assertTrue(testClass.removeSharedServer42InputRule(oldEndpoint, newEndpoint, oppositeNewEndpoint, true, true)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, newEndpoint, oppositeNewEndpoint, false, true)); } @Test @@ -201,94 +254,194 @@ public void hasOtherFlowRemoveSharedServer42InputRuleTest() { FlowEndpoint sameEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); FlowEndpoint changedSwitchEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_1); FlowEndpoint changedPortEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_2); - - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, sameEndpoint, true, false)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, sameEndpoint, true, true)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, sameEndpoint, false, false)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, changedSwitchEndpoint, true, false)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, changedSwitchEndpoint, true, true)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, changedSwitchEndpoint, false, false)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, changedPortEndpoint, true, false)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, changedPortEndpoint, true, true)); - assertFalse(testClass.removeSharedServer42InputRule(oldEndpoint, changedPortEndpoint, false, false)); + FlowEndpoint changedSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_1); + FlowEndpoint changedPortOppositeEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_3); + + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, sameEndpoint, changedSwitchOppositeEndpoint, true, false)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, sameEndpoint, changedPortOppositeEndpoint, true, true)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, sameEndpoint, changedSwitchOppositeEndpoint, false, false)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, changedSwitchEndpoint, changedSwitchOppositeEndpoint, true, false)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, changedSwitchEndpoint, changedPortOppositeEndpoint, true, true)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, changedSwitchEndpoint, changedSwitchOppositeEndpoint, false, false)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, changedPortEndpoint, changedSwitchOppositeEndpoint, true, false)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, changedPortEndpoint, changedPortOppositeEndpoint, true, true)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, changedPortEndpoint, changedSwitchOppositeEndpoint, false, false)); + assertFalse(testClass.removeSharedServer42InputRule( + oldEndpoint, changedPortEndpoint, sameEndpoint, true, false)); } @Test public void sameEndpointRemoveServer42OuterVlanMatchSharedRuleTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); + FlowEndpoint changedSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3); + FlowEndpoint sameSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_3); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, true, false)); + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, false, false)); + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, false, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, false, true)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, false, true)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, true, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); assertTrue(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, true, true)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, true, true)); } @Test public void changeSwitchRemoveServer42OuterVlanMatchSharedRuleTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_1); + FlowEndpoint changedSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3); + FlowEndpoint sameSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_3); + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, true, false)); + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, false, false)); + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, false, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, false, true)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, false, true)); assertTrue(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, true, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); assertTrue(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, true, true)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, true, true)); } @Test public void changeOuterVlanRemoveServer42OuterVlanMatchSharedRuleTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, VLAN_1); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, VLAN_2); + FlowEndpoint changedSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3); + FlowEndpoint sameSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_3); + + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, true, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, false, true)); + assertTrue(testClass.removeServer42OuterVlanMatchSharedRule( + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); + assertTrue(testClass.removeServer42OuterVlanMatchSharedRule( + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, true, true)); + } + + @Test + public void changePortRemoveServer42OuterVlanMatchSharedRuleTest() { + FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, VLAN_1); + FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_2, VLAN_1); + FlowEndpoint changedSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3); + FlowEndpoint sameSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_3); + + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, false, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, false, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, false, true)); assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, false, true)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); assertTrue(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, true, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, true, true)); + } + + @Test + public void swapEndpointsRemoveServer42OuterVlanMatchSharedRuleTest() { + FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, VLAN_1); + FlowEndpoint newEndpointMultiSwitch = new FlowEndpoint(SWITCH_ID_2, PORT_2, VLAN_2); + FlowEndpoint newEndpointOneSwitch = new FlowEndpoint(SWITCH_ID_1, PORT_2, VLAN_2); + + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + ONE_SWITCH_FLOW, oldEndpoint, newEndpointOneSwitch, oldEndpoint, true, false)); + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + ONE_SWITCH_FLOW, oldEndpoint, newEndpointOneSwitch, oldEndpoint, false, false)); + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + MULTI_SWITCH_FLOW, oldEndpoint, newEndpointMultiSwitch, oldEndpoint, false, false)); + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + MULTI_SWITCH_FLOW, oldEndpoint, newEndpointOneSwitch, oldEndpoint, false, true)); + assertFalse(testClass.removeServer42OuterVlanMatchSharedRule( + MULTI_SWITCH_FLOW, oldEndpoint, newEndpointMultiSwitch, oldEndpoint, true, false)); assertTrue(testClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, true, true)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpointOneSwitch, oldEndpoint, true, true)); } @Test public void hasOtherFlowRemoveServer42OuterVlanMatchSharedRuleTest() { FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, VLAN_1); FlowEndpoint newEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_2, VLAN_2); + FlowEndpoint changedSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_3); + FlowEndpoint sameSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_3); BaseFlowRuleRemovalAction mockTestClass = Mockito.mock(BaseFlowRuleRemovalAction.class); when(mockTestClass.findServer42OuterVlanMatchSharedRuleUsage(any())) .thenReturn(newArrayList(FLOW_ID_2)); - when(mockTestClass.removeServer42OuterVlanMatchSharedRule(any(), any(), any(), anyBoolean(), anyBoolean())) - .thenCallRealMethod(); + when(mockTestClass.removeServer42OuterVlanMatchSharedRule( + any(), any(), any(), any(), anyBoolean(), anyBoolean())).thenCallRealMethod(); assertFalse(mockTestClass.removeServer42OuterVlanMatchSharedRule( - ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, true, false)); + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); assertFalse(mockTestClass.removeServer42OuterVlanMatchSharedRule( - ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, false, false)); + ONE_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(mockTestClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, false, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, false, false)); assertFalse(mockTestClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, false, true)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, false, true)); assertFalse(mockTestClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, true, false)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, changedSwitchOppositeEndpoint, true, false)); assertFalse(mockTestClass.removeServer42OuterVlanMatchSharedRule( - MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, true, true)); + MULTI_SWITCH_FLOW, oldEndpoint, newEndpoint, sameSwitchOppositeEndpoint, true, true)); + } + + @Test + public void removeOuterVlanMatchSharedRuleTest() { + FlowEndpoint oldEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, VLAN_1); + FlowEndpoint changeSwitchEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_1, VLAN_1); + FlowEndpoint changeVlanEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_1, VLAN_2); + FlowEndpoint changePortEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_2, VLAN_1); + FlowEndpoint changedSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_2, PORT_1, VLAN_1); + FlowEndpoint sameSwitchOppositeEndpoint = new FlowEndpoint(SWITCH_ID_1, PORT_3, VLAN_1); + + assertFalse(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, oldEndpoint, changedSwitchOppositeEndpoint)); + assertFalse(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, oldEndpoint, sameSwitchOppositeEndpoint)); + + assertTrue(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changeSwitchEndpoint, changedSwitchOppositeEndpoint)); + assertTrue(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changeVlanEndpoint, changedSwitchOppositeEndpoint)); + assertTrue(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changePortEndpoint, changedSwitchOppositeEndpoint)); + + assertTrue(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changeSwitchEndpoint, sameSwitchOppositeEndpoint)); + assertTrue(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changeVlanEndpoint, sameSwitchOppositeEndpoint)); + assertTrue(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changePortEndpoint, sameSwitchOppositeEndpoint)); + + assertFalse(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changeSwitchEndpoint, oldEndpoint)); + assertFalse(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changeVlanEndpoint, oldEndpoint)); + assertFalse(testClass.removeOuterVlanMatchSharedRule( + FLOW_ID_1, oldEndpoint, changePortEndpoint, oldEndpoint)); } private static class TestClass extends BaseFlowRuleRemovalAction { 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 800902047b2..6bca898ee9d 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 @@ -93,6 +93,23 @@ public interface KafkaTopicsConfig { @Default("kilda.speaker.flowhs.priv.region") String getFlowHsSpeakerRegionTopic(); + @Key("speaker.switch.manager") + @Default("kilda.speaker.switch.manager.storm") + String getSpeakerSwitchManagerTopic(); + + @Key("speaker.switch.manager.region") + @FallbackKey("kafka.speaker.switch.manager.region") + @Default("kilda.speaker.switch.manager") + String getSpeakerSwitchManagerRegionTopic(); + + @Key("speaker.switch.manager.priv") + @Default("kilda.speaker.switch.manager.priv") + String getSwitchManagerSpeakerTopic(); + + @Key("speaker.switch.manager.priv.region") + @Default("kilda.speaker.switch.manager.priv.region") + String getSwitchManagerSpeakerRegionTopic(); + @Key("speaker.flow.ping") @FallbackKey("kafka.speaker.flow.ping") @Default("kilda.speaker.flow.ping.storm") diff --git a/src-java/kilda-model/src/main/java/org/openkilda/model/FlowEndpoint.java b/src-java/kilda-model/src/main/java/org/openkilda/model/FlowEndpoint.java index eab12ff8ba6..67940e5b600 100644 --- a/src-java/kilda-model/src/main/java/org/openkilda/model/FlowEndpoint.java +++ b/src-java/kilda-model/src/main/java/org/openkilda/model/FlowEndpoint.java @@ -123,4 +123,8 @@ public boolean isSwitchPortVlanEquals(FlowEndpoint other) { return isSwitchPortEquals(other) && Objects.equals(getVlanStack(), other.getVlanStack()); } + + public boolean isSwitchPortOuterVlanEquals(FlowEndpoint other) { + return isSwitchPortEquals(other) && outerVlanId == other.getOuterVlanId(); + } } diff --git a/src-java/network-topology/network-storm-topology/src/main/java/org/openkilda/wfm/topology/network/NetworkTopology.java b/src-java/network-topology/network-storm-topology/src/main/java/org/openkilda/wfm/topology/network/NetworkTopology.java index df7d3035017..8fdd2e1c0fb 100644 --- a/src-java/network-topology/network-storm-topology/src/main/java/org/openkilda/wfm/topology/network/NetworkTopology.java +++ b/src-java/network-topology/network-storm-topology/src/main/java/org/openkilda/wfm/topology/network/NetworkTopology.java @@ -170,7 +170,7 @@ private void workerSpeakerRules(TopologyBuilder topology) { declareBolt(topology, speakerRulesWorker, SpeakerRulesWorker.BOLT_ID) .directGrouping(CoordinatorBolt.ID) .fieldsGrouping(workerConfig.getHubComponent(), IslHandler.STREAM_SPEAKER_RULES_ID, keyGrouping) - .fieldsGrouping(workerConfig.getWorkerSpoutComponent(), + .fieldsGrouping(SpeakerRulesRouter.BOLT_ID, SpeakerRulesRouter.STREAM_WORKER_ID, keyGrouping); } @@ -206,7 +206,7 @@ private void workerSwitchManager(TopologyBuilder topology) { declareBolt(topology, switchManagerWorker, SwitchManagerWorker.BOLT_ID) .directGrouping(CoordinatorBolt.ID) .fieldsGrouping(workerConfig.getHubComponent(), SwitchHandler.STREAM_SWMANAGER_ID, keyGrouping) - .fieldsGrouping(workerConfig.getWorkerSpoutComponent(), + .fieldsGrouping(SwitchManagerRouter.BOLT_ID, SwitchManagerRouter.STREAM_WORKER_ID, keyGrouping); } @@ -310,7 +310,7 @@ private void bfdWorker(TopologyBuilder topology) { .directGrouping(CoordinatorBolt.ID) .fieldsGrouping(workerConfig.getHubComponent(), BfdHub.STREAM_WORKER_ID, keyGrouping) .fieldsGrouping( - workerConfig.getWorkerSpoutComponent(), SpeakerRouter.STREAM_BFD_WORKER_ID, keyGrouping) + SpeakerRouter.BOLT_ID, SpeakerRouter.STREAM_BFD_WORKER_ID, keyGrouping) .fieldsGrouping(GrpcRouter.BOLT_ID, GrpcRouter.STREAM_BFD_WORKER_ID, keyGrouping); } diff --git a/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v1/switches/LogicalPortsSyncDto.java b/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v1/switches/LogicalPortsSyncDto.java index e9246d88b42..dc426f00b3d 100644 --- a/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v1/switches/LogicalPortsSyncDto.java +++ b/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v1/switches/LogicalPortsSyncDto.java @@ -34,4 +34,5 @@ public class LogicalPortsSyncDto { private List excess; private List installed; private List removed; + private String error; } diff --git a/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v1/switches/LogicalPortsValidationDto.java b/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v1/switches/LogicalPortsValidationDto.java index d9eb01a3f68..e195751583d 100644 --- a/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v1/switches/LogicalPortsValidationDto.java +++ b/src-java/northbound-service/northbound-api/src/main/java/org/openkilda/northbound/dto/v1/switches/LogicalPortsValidationDto.java @@ -32,4 +32,5 @@ public class LogicalPortsValidationDto { private List misconfigured; private List proper; private List excess; + private String error; } diff --git a/src-java/northbound-service/northbound-api/src/test/java/org/openkilda/northbound/dto/JsonSerializationTest.java b/src-java/northbound-service/northbound-api/src/test/java/org/openkilda/northbound/dto/JsonSerializationTest.java index b3c7d05660a..bb4ea351c2d 100644 --- a/src-java/northbound-service/northbound-api/src/test/java/org/openkilda/northbound/dto/JsonSerializationTest.java +++ b/src-java/northbound-service/northbound-api/src/test/java/org/openkilda/northbound/dto/JsonSerializationTest.java @@ -149,7 +149,7 @@ public void switchSyncResultTest() throws IOException { GroupsSyncDto groups = new GroupsSyncDto(emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptyList(), emptyList()); LogicalPortsSyncDto logicalPorts = new LogicalPortsSyncDto(emptyList(), emptyList(), emptyList(), emptyList(), - emptyList(), emptyList()); + emptyList(), emptyList(), ""); SwitchSyncResult dto = new SwitchSyncResult(rules, meters, groups, logicalPorts); assertEquals(dto, pass(dto, SwitchSyncResult.class)); } @@ -161,7 +161,7 @@ public void switchValidateResultTest() throws IOException { MetersValidationDto meters = new MetersValidationDto(emptyList(), emptyList(), emptyList(), emptyList()); GroupsValidationDto groups = new GroupsValidationDto(emptyList(), emptyList(), emptyList(), emptyList()); LogicalPortsValidationDto logicalPorts = new LogicalPortsValidationDto( - emptyList(), emptyList(), emptyList(), emptyList()); + emptyList(), emptyList(), emptyList(), emptyList(), ""); SwitchValidationResult dto = new SwitchValidationResult(rules, meters, groups, logicalPorts); assertEquals(dto, pass(dto, SwitchValidationResult.class)); } diff --git a/src-java/rule-manager/rule-manager-api/src/main/java/org/openkilda/rulemanager/GroupSpeakerData.java b/src-java/rule-manager/rule-manager-api/src/main/java/org/openkilda/rulemanager/GroupSpeakerData.java index 371c2adeb5b..2aa349dc2e8 100644 --- a/src-java/rule-manager/rule-manager-api/src/main/java/org/openkilda/rulemanager/GroupSpeakerData.java +++ b/src-java/rule-manager/rule-manager-api/src/main/java/org/openkilda/rulemanager/GroupSpeakerData.java @@ -26,6 +26,7 @@ import com.fasterxml.jackson.databind.annotation.JsonNaming; import com.fasterxml.jackson.databind.annotation.JsonSerialize; import lombok.EqualsAndHashCode; +import lombok.ToString; import lombok.Value; import lombok.experimental.SuperBuilder; @@ -38,6 +39,7 @@ @JsonSerialize @SuperBuilder(toBuilder = true) @JsonNaming(SnakeCaseStrategy.class) +@ToString(callSuper = true) public class GroupSpeakerData extends SpeakerData { GroupId groupId; GroupType type; diff --git a/src-java/rule-manager/rule-manager-implementation/src/main/java/org/openkilda/rulemanager/factory/generator/service/BroadCastDiscoveryRuleGenerator.java b/src-java/rule-manager/rule-manager-implementation/src/main/java/org/openkilda/rulemanager/factory/generator/service/BroadCastDiscoveryRuleGenerator.java index 09b737c6400..6c43646783e 100644 --- a/src-java/rule-manager/rule-manager-implementation/src/main/java/org/openkilda/rulemanager/factory/generator/service/BroadCastDiscoveryRuleGenerator.java +++ b/src-java/rule-manager/rule-manager-implementation/src/main/java/org/openkilda/rulemanager/factory/generator/service/BroadCastDiscoveryRuleGenerator.java @@ -47,6 +47,8 @@ import org.openkilda.rulemanager.action.SetFieldAction; import org.openkilda.rulemanager.group.Bucket; import org.openkilda.rulemanager.group.GroupType; +import org.openkilda.rulemanager.group.WatchGroup; +import org.openkilda.rulemanager.group.WatchPort; import org.openkilda.rulemanager.match.FieldMatch; import com.google.common.collect.Sets; @@ -139,11 +141,15 @@ private static GroupSpeakerData getRoundTripLatencyGroup(Switch sw) { // todo: remove useless set ETH_DST action actionSetDstMac(sw.getSwitchId().toLong()), new PortOutAction(new PortNumber(SpecialPortType.CONTROLLER)))) + .watchGroup(WatchGroup.ANY) + .watchPort(WatchPort.ANY) .build()); buckets.add(Bucket.builder() .writeActions(Sets.newHashSet( SetFieldAction.builder().field(Field.UDP_DST).value(LATENCY_PACKET_UDP_PORT).build(), new PortOutAction(new PortNumber(SpecialPortType.IN_PORT)))) + .watchGroup(WatchGroup.ANY) + .watchPort(WatchPort.ANY) .build()); return GroupSpeakerData.builder() diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/StreamType.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/StreamType.java index 03f01bd3865..4ad77a61498 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/StreamType.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/StreamType.java @@ -18,6 +18,7 @@ public enum StreamType { TO_NORTHBOUND, TO_FLOODLIGHT, + REQUEST_TO_FLOODLIGHT, TO_GRPC, HUB_TO_METRICS_BOLT } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/SwitchManagerTopology.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/SwitchManagerTopology.java index 3bfb46ef6c8..a707130e31a 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/SwitchManagerTopology.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/SwitchManagerTopology.java @@ -15,10 +15,11 @@ package org.openkilda.wfm.topology.switchmanager; +import org.openkilda.messaging.AbstractMessage; import org.openkilda.persistence.PersistenceManager; import org.openkilda.rulemanager.RuleManagerConfig; import org.openkilda.wfm.LaunchEnvironment; -import org.openkilda.wfm.share.flow.resources.FlowResourcesConfig; +import org.openkilda.wfm.kafka.AbstractMessageSerializer; import org.openkilda.wfm.share.hubandspoke.CoordinatorBolt; import org.openkilda.wfm.share.hubandspoke.CoordinatorSpout; import org.openkilda.wfm.share.hubandspoke.HubBolt; @@ -45,8 +46,10 @@ public class SwitchManagerTopology extends AbstractTopology flKafkaBolt = makeKafkaBolt( + topologyConfig.getKafkaSpeakerSwitchManagerTopic(), AbstractMessageSerializer.class); + declareBolt(builder, flKafkaBolt, SPEAKER_REQUEST_KAFKA_BOLT) + .shuffleGrouping(SpeakerWorkerBolt.ID, StreamType.REQUEST_TO_FLOODLIGHT.toString()); + declareBolt(builder, buildKafkaBolt(topologyConfig.getGrpcSpeakerTopic()), GRPC_SPEAKER_KAFKA_BOLT) .shuffleGrouping(SpeakerWorkerBolt.ID, StreamType.TO_GRPC.toString()); diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/SwitchManagerTopologyConfig.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/SwitchManagerTopologyConfig.java index 2d87ccd6ceb..b44edef2d53 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/SwitchManagerTopologyConfig.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/SwitchManagerTopologyConfig.java @@ -47,6 +47,14 @@ default String getKafkaSpeakerTopic() { return getKafkaTopics().getSpeakerTopic(); } + default String getKafkaSpeakerSwitchManagerTopic() { + return getKafkaTopics().getSpeakerSwitchManagerTopic(); + } + + default String getKafkaSwitchManagerSpeakerWorkerTopic() { + return getKafkaTopics().getSwitchManagerSpeakerTopic(); + } + default String getGrpcSpeakerTopic() { return getKafkaTopics().getGrpcSpeakerTopic(); } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/bolt/SpeakerWorkerBolt.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/bolt/SpeakerWorkerBolt.java index 053564c1e4c..c37897eb817 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/bolt/SpeakerWorkerBolt.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/bolt/SpeakerWorkerBolt.java @@ -15,6 +15,9 @@ package org.openkilda.wfm.topology.switchmanager.bolt; +import org.openkilda.floodlight.api.request.SpeakerRequest; +import org.openkilda.floodlight.api.request.rulemanager.BaseSpeakerCommandsRequest; +import org.openkilda.floodlight.api.response.SpeakerResponse; import org.openkilda.messaging.Message; import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.command.CommandData; @@ -35,6 +38,7 @@ public class SpeakerWorkerBolt extends WorkerBolt implements SpeakerCommandCarri public static final String ID = "speaker.worker.bolt"; public static final String INCOME_STREAM = "speaker.worker.stream"; + public static final String OF_COMMANDS_INCOME_STREAM = "speaker.worker.of.commands.stream"; public static final String FIELD_ID_COOKIE = "cookie"; @@ -53,22 +57,37 @@ protected void init() { @Override protected void onHubRequest(Tuple input) throws PipelineException { String key = input.getStringByField(MessageKafkaTranslator.FIELD_ID_KEY); - CommandData command = pullValue(input, MessageKafkaTranslator.FIELD_ID_PAYLOAD, CommandData.class); MessageCookie cookie = pullValue(input, FIELD_ID_COOKIE, MessageCookie.class); - - if (command instanceof GrpcBaseRequest) { - service.sendGrpcCommand(key, command, cookie); + if (INCOME_STREAM.equals(input.getSourceStreamId())) { + CommandData command = pullValue(input, MessageKafkaTranslator.FIELD_ID_PAYLOAD, CommandData.class); + + if (command instanceof GrpcBaseRequest) { + service.sendGrpcCommand(key, command, cookie); + } else { + service.sendFloodlightCommand(key, command, cookie); + } + } else if (OF_COMMANDS_INCOME_STREAM.equals(input.getSourceStreamId())) { + BaseSpeakerCommandsRequest request = pullValue(input, MessageKafkaTranslator.FIELD_ID_PAYLOAD, + BaseSpeakerCommandsRequest.class); + service.sendFloodlightOfRequest(key, request, cookie); } else { - service.sendFloodlightCommand(key, command, cookie); + unhandledInput(input); } } @Override protected void onAsyncResponse(Tuple request, Tuple response) throws Exception { String key = pullKey(); - Message message = pullValue(response, MessageKafkaTranslator.FIELD_ID_PAYLOAD, Message.class); - - service.handleResponse(key, message); + Object payload = pullValue(response, MessageKafkaTranslator.FIELD_ID_PAYLOAD, Object.class); + if (payload instanceof Message) { + Message message = (Message) payload; + service.handleResponse(key, message); + } else if (payload instanceof SpeakerResponse) { + SpeakerResponse speakerResponse = (SpeakerResponse) payload; + service.handleSpeakerResponse(key, speakerResponse); + } else { + unhandledInput(response); + } } @Override @@ -80,6 +99,7 @@ protected void onRequestTimeout(Tuple request) throws PipelineException { public void declareOutputFields(OutputFieldsDeclarer declarer) { super.declareOutputFields(declarer); declarer.declareStream(StreamType.TO_FLOODLIGHT.toString(), MessageKafkaTranslator.STREAM_FIELDS); + declarer.declareStream(StreamType.REQUEST_TO_FLOODLIGHT.toString(), MessageKafkaTranslator.STREAM_FIELDS); declarer.declareStream(StreamType.TO_GRPC.toString(), MessageKafkaTranslator.STREAM_FIELDS); } @@ -88,6 +108,11 @@ public void sendFloodlightCommand(String key, CommandMessage command) { emitWithContext(StreamType.TO_FLOODLIGHT.toString(), getCurrentTuple(), new Values(key, command)); } + @Override + public void sendFloodlightOfRequest(String key, SpeakerRequest request) { + emitWithContext(StreamType.REQUEST_TO_FLOODLIGHT.toString(), getCurrentTuple(), new Values(key, request)); + } + @Override public void sendGrpcCommand(String key, CommandMessage command) { emitWithContext(StreamType.TO_GRPC.toString(), getCurrentTuple(), new Values(key, command)); @@ -98,4 +123,10 @@ public void sendResponse(String key, Message response) { Values values = new Values(key, response, getCommandContext()); emitResponseToHub(getCurrentTuple(), values); } + + @Override + public void sendSpeakerResponse(String key, SpeakerResponse response) { + Values values = new Values(key, response, getCommandContext()); + emitResponseToHub(getCurrentTuple(), values); + } } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/bolt/SwitchManagerHub.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/bolt/SwitchManagerHub.java index e2a482ee04b..27da59bce16 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/bolt/SwitchManagerHub.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/bolt/SwitchManagerHub.java @@ -15,8 +15,19 @@ package org.openkilda.wfm.topology.switchmanager.bolt; +import static java.lang.String.format; + import org.openkilda.bluegreen.LifecycleEvent; +import org.openkilda.floodlight.api.request.rulemanager.BaseSpeakerCommandsRequest; +import org.openkilda.floodlight.api.request.rulemanager.DeleteSpeakerCommandsRequest; +import org.openkilda.floodlight.api.request.rulemanager.InstallSpeakerCommandsRequest; +import org.openkilda.floodlight.api.request.rulemanager.ModifySpeakerCommandsRequest; +import org.openkilda.floodlight.api.request.rulemanager.OfCommand; +import org.openkilda.floodlight.api.request.rulemanager.Origin; +import org.openkilda.floodlight.api.response.rulemanager.SpeakerCommandResponse; +import org.openkilda.messaging.AbstractMessage; import org.openkilda.messaging.Message; +import org.openkilda.messaging.MessageContext; import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.command.CommandData; import org.openkilda.messaging.command.CommandMessage; @@ -38,7 +49,6 @@ import org.openkilda.wfm.error.MessageDispatchException; import org.openkilda.wfm.error.PipelineException; import org.openkilda.wfm.error.UnexpectedInputException; -import org.openkilda.wfm.share.flow.resources.FlowResourcesConfig; import org.openkilda.wfm.share.hubandspoke.HubBolt; import org.openkilda.wfm.share.utils.KeyProvider; import org.openkilda.wfm.share.zk.ZkStreams; @@ -68,7 +78,9 @@ import org.apache.storm.tuple.Values; import java.util.HashMap; +import java.util.List; import java.util.Map; +import java.util.UUID; import java.util.function.Consumer; import java.util.function.Function; @@ -97,7 +109,6 @@ public class SwitchManagerHub extends HubBolt implements SwitchManagerCarrier { public static final Fields ZOOKEEPER_STREAM_FIELDS = new Fields( ZooKeeperBolt.FIELD_ID_STATE, ZooKeeperBolt.FIELD_ID_CONTEXT); - private final FlowResourcesConfig flowResourcesConfig; private final SwitchManagerTopologyConfig topologyConfig; private final RuleManagerConfig ruleManagerConfig; @@ -115,11 +126,9 @@ public class SwitchManagerHub extends HubBolt implements SwitchManagerCarrier { public SwitchManagerHub(HubBolt.Config hubConfig, PersistenceManager persistenceManager, SwitchManagerTopologyConfig topologyConfig, - FlowResourcesConfig flowResourcesConfig, RuleManagerConfig ruleManagerConfig) { super(persistenceManager, hubConfig); this.topologyConfig = topologyConfig; - this.flowResourcesConfig = flowResourcesConfig; this.ruleManagerConfig = ruleManagerConfig; enableMeterRegistry("kilda.switch_validate", StreamType.HUB_TO_METRICS_BOLT.name()); @@ -149,7 +158,7 @@ public void init() { new ValidationServiceImpl(persistenceManager, new RuleManagerImpl(ruleManagerConfig)))); syncService = registerService( serviceRegistry, "switch-sync", this, - carrier -> new SwitchSyncService(carrier, persistenceManager, flowResourcesConfig)); + carrier -> new SwitchSyncService(carrier, persistenceManager)); switchRuleService = registerService( serviceRegistry, "switch-rules", this, carrier -> new SwitchRuleService(carrier, persistenceManager.getRepositoryFactory())); @@ -249,7 +258,19 @@ private SwitchManagerHubService getService(MessageCookie cookie) throws MessageD } private void dispatchResponse(Tuple input) throws PipelineException { - Message message = pullValue(input, MessageKafkaTranslator.FIELD_ID_PAYLOAD, Message.class); + Object payload = pullValue(input, MessageKafkaTranslator.FIELD_ID_PAYLOAD, Object.class); + if (payload instanceof Message) { + Message message = (Message) payload; + dispatchResponse(message, input); + } else if (payload instanceof AbstractMessage) { + AbstractMessage abstractMessage = (AbstractMessage) payload; + dispatchResponse(abstractMessage); + } else { + unhandledInput(input); + } + } + + private void dispatchResponse(Message message, Tuple input) { MessageCookie cookie = message.getCookie(); if (cookie == null) { log.error("There is no message cookie in response, can't determine target service (response: {})", message); @@ -276,6 +297,27 @@ private void dispatchResponse(Tuple input) throws PipelineException { } } + private void dispatchResponse(AbstractMessage message) { + MessageCookie cookie = message.getMessageCookie(); + if (cookie == null) { + log.error("There is no message cookie in response, can't determine target service (response: {})", message); + return; + } + + try { + SwitchManagerHubService service = getService(cookie); + if (message instanceof SpeakerCommandResponse) { + service.dispatchWorkerMessage((SpeakerCommandResponse) message, cookie.getNested()); + } else { + throw new UnexpectedInputException(message); + } + } catch (MessageDispatchException e) { + log.warn("Unable to route worker response {}: {}", message, e.getMessage(message.getMessageCookie())); + } catch (UnexpectedInputException e) { + log.error("{}", e.getMessage(message.getMessageCookie()), e); + } + } + private void dispatchTimeout(MessageCookie cookie) { SwitchManagerHubService service = serviceRegistry.get(cookie); if (service == null) { @@ -345,6 +387,46 @@ public void runHeavyOperation(SwitchId switchId, @NonNull MessageCookie messageC emit(HeavyOperationBolt.INCOME_STREAM, getCurrentTuple(), makeHeavyOperationTuple(switchId, messageCookie)); } + @Override + public void sendOfCommandsToSpeaker(String key, List commands, OfCommandAction action, + SwitchId switchId) { + // will never be user, because of carrier decorator + sendOfCommandsToSpeaker(commands, action, switchId, new MessageCookie(key)); + } + + @Override + public void sendOfCommandsToSpeaker(List commands, OfCommandAction action, SwitchId switchId, + @NonNull MessageCookie cookie) { + sendOfCommandsToSpeaker(cookie.toString(), commands, action, switchId, cookie); + } + + private void sendOfCommandsToSpeaker(String requestKey, List commands, OfCommandAction action, + SwitchId switchId, MessageCookie cookie) { + BaseSpeakerCommandsRequest request = getRequest(requestKey, commands, action, switchId); + + emit(SpeakerWorkerBolt.OF_COMMANDS_INCOME_STREAM, getCurrentTuple(), + makeWorkerTuple(requestKey, request, cookie)); + } + + private BaseSpeakerCommandsRequest getRequest(String requestKey, List commands, OfCommandAction action, + SwitchId switchId) { + MessageContext messageContext = new MessageContext(requestKey, getCommandContext().getCorrelationId()); + UUID commandId = UUID.randomUUID(); + switch (action) { + case INSTALL: + return new InstallSpeakerCommandsRequest(messageContext, switchId, commandId, commands, + Origin.SW_MANAGER); + case MODIFY: + return new ModifySpeakerCommandsRequest(messageContext, switchId, commandId, commands, + Origin.SW_MANAGER); + case DELETE: + return new DeleteSpeakerCommandsRequest(messageContext, switchId, commandId, commands, + Origin.SW_MANAGER); + default: + throw new IllegalStateException(format("Unknown OpenFlow command type %s", action)); + } + } + @Override public void response(String key, Message message) { emit(NORTHBOUND_STREAM_ID, getCurrentTuple(), makeNorthboundTuple(key, message)); @@ -383,6 +465,7 @@ public void sendInactive() { public void declareOutputFields(OutputFieldsDeclarer declarer) { super.declareOutputFields(declarer); declarer.declareStream(SpeakerWorkerBolt.INCOME_STREAM, WORKER_STREAM_FIELDS); + declarer.declareStream(SpeakerWorkerBolt.OF_COMMANDS_INCOME_STREAM, WORKER_STREAM_FIELDS); declarer.declareStream(NORTHBOUND_STREAM_ID, NORTHBOUND_STREAM_FIELDS); declarer.declareStream(ZOOKEEPER_STREAM_ID, ZOOKEEPER_STREAM_FIELDS); declarer.declareStream(HeavyOperationBolt.INCOME_STREAM, HEAVY_OPERATION_STREAM_FIELDS); @@ -392,6 +475,10 @@ private Values makeWorkerTuple(String key, CommandData payload, MessageCookie co return new Values(KeyProvider.generateChainedKey(key), payload, cookie, getCommandContext()); } + private Values makeWorkerTuple(String key, BaseSpeakerCommandsRequest payload, MessageCookie cookie) { + return new Values(KeyProvider.generateChainedKey(key), payload, cookie, getCommandContext()); + } + private Values makeHeavyOperationTuple(SwitchId switchId, MessageCookie cookie) { return new Values(cookie.getValue(), switchId, cookie, getCommandContext()); } @@ -407,4 +494,10 @@ private static S registerService( registry.put(new MessageCookie(name), service); return service; } + + public enum OfCommandAction { + INSTALL, + MODIFY, + DELETE + } } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/fsm/SwitchSyncFsm.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/fsm/SwitchSyncFsm.java index fe568c2fa91..c77cbea4892 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/fsm/SwitchSyncFsm.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/fsm/SwitchSyncFsm.java @@ -15,19 +15,13 @@ package org.openkilda.wfm.topology.switchmanager.fsm; +import static java.lang.String.format; import static java.util.Collections.emptyList; import static org.apache.storm.shade.org.apache.commons.collections.ListUtils.union; +import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.COMMANDS_PROCESSED; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.ERROR; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.EXCESS_RULES_REMOVED; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.GROUPS_INSTALLED; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.GROUPS_MODIFIED; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.GROUPS_REMOVED; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.LOGICAL_PORT_INSTALLED; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.LOGICAL_PORT_REMOVED; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.METERS_REMOVED; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.MISCONFIGURED_METERS_MODIFIED; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.MISCONFIGURED_RULES_REINSTALLED; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.MISSING_RULES_INSTALLED; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.NEXT; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent.TIMEOUT; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.COMPUTE_EXCESS_METERS; @@ -36,34 +30,28 @@ import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.COMPUTE_LOGICAL_PORTS_COMMANDS; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.COMPUTE_MISCONFIGURED_METERS; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.COMPUTE_MISCONFIGURED_RULES; +import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.COMPUTE_MISSING_METERS; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.COMPUTE_MISSING_RULES; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.FINISHED; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.FINISHED_WITH_ERROR; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.GROUPS_COMMANDS_SEND; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.INITIALIZED; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.LOGICAL_PORTS_COMMANDS_SEND; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.METERS_COMMANDS_SEND; -import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.RULES_COMMANDS_SEND; - -import org.openkilda.messaging.command.flow.BaseFlow; -import org.openkilda.messaging.command.flow.InstallFlowForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.ModifyDefaultMeterForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.ReinstallDefaultFlowForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.RemoveFlow; -import org.openkilda.messaging.command.flow.RemoveFlowForSwitchManagerRequest; +import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.SEND_INSTALL_COMMANDS; +import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.SEND_MODIFY_COMMANDS; +import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState.SEND_REMOVE_COMMANDS; + +import org.openkilda.floodlight.api.request.rulemanager.FlowCommand; +import org.openkilda.floodlight.api.request.rulemanager.GroupCommand; +import org.openkilda.floodlight.api.request.rulemanager.MeterCommand; +import org.openkilda.floodlight.api.request.rulemanager.OfCommand; import org.openkilda.messaging.command.grpc.CreateOrUpdateLogicalPortRequest; import org.openkilda.messaging.command.grpc.DeleteLogicalPortRequest; -import org.openkilda.messaging.command.switches.DeleteGroupRequest; -import org.openkilda.messaging.command.switches.DeleterMeterForSwitchManagerRequest; -import org.openkilda.messaging.command.switches.InstallGroupRequest; -import org.openkilda.messaging.command.switches.ModifyGroupRequest; import org.openkilda.messaging.command.switches.SwitchValidateRequest; import org.openkilda.messaging.error.ErrorData; import org.openkilda.messaging.error.ErrorMessage; import org.openkilda.messaging.error.ErrorType; import org.openkilda.messaging.error.rule.SwitchSyncErrorData; import org.openkilda.messaging.info.InfoMessage; -import org.openkilda.messaging.info.flow.FlowReinstallResponse; import org.openkilda.messaging.info.switches.GroupInfoEntry; import org.openkilda.messaging.info.switches.GroupSyncEntry; import org.openkilda.messaging.info.switches.LogicalPortInfoEntry; @@ -72,15 +60,15 @@ import org.openkilda.messaging.info.switches.MetersSyncEntry; import org.openkilda.messaging.info.switches.RulesSyncEntry; import org.openkilda.messaging.info.switches.SwitchSyncResponse; -import org.openkilda.model.GroupId; -import org.openkilda.model.MeterId; -import org.openkilda.model.MirrorConfig; import org.openkilda.model.SwitchId; -import org.openkilda.model.cookie.Cookie; +import org.openkilda.rulemanager.FlowSpeakerData; +import org.openkilda.rulemanager.GroupSpeakerData; +import org.openkilda.rulemanager.MeterSpeakerData; +import org.openkilda.rulemanager.SpeakerData; import org.openkilda.wfm.share.utils.AbstractBaseFsm; +import org.openkilda.wfm.topology.switchmanager.bolt.SwitchManagerHub.OfCommandAction; import org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent; import org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState; -import org.openkilda.wfm.topology.switchmanager.model.GroupInstallContext; import org.openkilda.wfm.topology.switchmanager.model.ValidateGroupsResult; import org.openkilda.wfm.topology.switchmanager.model.ValidateLogicalPortsResult; import org.openkilda.wfm.topology.switchmanager.model.ValidateMetersResult; @@ -96,6 +84,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Optional; +import java.util.Set; +import java.util.UUID; import java.util.stream.Collectors; @Slf4j @@ -113,28 +103,15 @@ public class SwitchSyncFsm extends AbstractBaseFsm installedRulesCookies; + private List reinstalledRulesCookies; private List removedFlowRulesCookies; - private final List removedDefaultRulesCookies = new ArrayList<>(); - - private List missingRules = emptyList(); - private List misconfiguredRules = emptyList(); - private List misconfiguredMeters = emptyList(); - private List excessRules = emptyList(); - private List excessMeters = emptyList(); - private List missingGroups = emptyList(); - private List misconfiguredGroups = emptyList(); - private List excessGroups = emptyList(); + + private final List toInstall = new ArrayList<>(); + private final List toModify = new ArrayList<>(); + private final List toRemove = new ArrayList<>(); private List missingLogicalPorts = emptyList(); private List excessLogicalPorts = emptyList(); - private int missingRulesPendingResponsesCount = 0; - private int excessRulesPendingResponsesCount = 0; - private int reinstallDefaultRulesPendingResponsesCount = 0; - private int excessMetersPendingResponsesCount = 0; - private int misconfiguredMetersPendingResponsesCount = 0; - private int missingGroupsPendingResponsesCount = 0; - private int misconfiguredGroupsPendingResponsesCount = 0; - private int excessGroupsPendingResponsesCount = 0; private int missingLogicalPortsPendingResponsesCount = 0; private int excessLogicalPortsPendingResponsesCount = 0; @@ -191,7 +168,12 @@ SwitchSyncEvent, Object> builder() { builder.externalTransition().from(COMPUTE_MISCONFIGURED_METERS).to(FINISHED_WITH_ERROR).on(ERROR) .callMethod(FINISHED_WITH_ERROR_METHOD_NAME); - builder.externalTransition().from(COMPUTE_MISCONFIGURED_METERS).to(COMPUTE_GROUP_MIRROR_CONFIGS).on(NEXT) + builder.externalTransition().from(COMPUTE_MISCONFIGURED_METERS).to(COMPUTE_MISSING_METERS).on(NEXT) + .callMethod("computeMissingMeters"); + builder.externalTransition().from(COMPUTE_MISSING_METERS).to(FINISHED_WITH_ERROR).on(ERROR) + .callMethod(FINISHED_WITH_ERROR_METHOD_NAME); + + builder.externalTransition().from(COMPUTE_MISSING_METERS).to(COMPUTE_GROUP_MIRROR_CONFIGS).on(NEXT) .callMethod("computeGroupMirrorConfigs"); builder.externalTransition().from(COMPUTE_GROUP_MIRROR_CONFIGS).to(FINISHED_WITH_ERROR).on(ERROR) .callMethod(FINISHED_WITH_ERROR_METHOD_NAME); @@ -202,7 +184,7 @@ SwitchSyncEvent, Object> builder() { .callMethod(FINISHED_WITH_ERROR_METHOD_NAME); builder.externalTransition().from(COMPUTE_LOGICAL_PORTS_COMMANDS).to(LOGICAL_PORTS_COMMANDS_SEND).on(NEXT) - .callMethod("sendLogicalPortsCommandsCommands"); + .callMethod("sendLogicalPortsCommands"); builder.internalTransition().within(LOGICAL_PORTS_COMMANDS_SEND).on(LOGICAL_PORT_INSTALLED) .callMethod("logicalPortInstalled"); builder.internalTransition().within(LOGICAL_PORTS_COMMANDS_SEND).on(LOGICAL_PORT_REMOVED) @@ -212,42 +194,31 @@ SwitchSyncEvent, Object> builder() { builder.externalTransition().from(LOGICAL_PORTS_COMMANDS_SEND).to(FINISHED_WITH_ERROR).on(ERROR) .callMethod(FINISHED_WITH_ERROR_METHOD_NAME); - builder.externalTransition().from(LOGICAL_PORTS_COMMANDS_SEND).to(GROUPS_COMMANDS_SEND).on(NEXT) - .callMethod("sendGroupsCommands"); - builder.internalTransition().within(GROUPS_COMMANDS_SEND).on(GROUPS_INSTALLED).callMethod("groupsInstalled"); - builder.internalTransition().within(GROUPS_COMMANDS_SEND).on(GROUPS_MODIFIED).callMethod("groupsModified"); - builder.internalTransition().within(GROUPS_COMMANDS_SEND).on(GROUPS_REMOVED).callMethod("groupsRemoved"); + builder.externalTransition().from(LOGICAL_PORTS_COMMANDS_SEND).to(SEND_REMOVE_COMMANDS).on(NEXT) + .callMethod("sendRemoveCommands"); - builder.externalTransition().from(GROUPS_COMMANDS_SEND).to(FINISHED_WITH_ERROR).on(TIMEOUT) + builder.externalTransition().from(SEND_REMOVE_COMMANDS).to(FINISHED_WITH_ERROR).on(TIMEOUT) .callMethod(COMMANDS_PROCESSING_FAILED_BY_TIMEOUT_METHOD_NAME); - builder.externalTransition().from(GROUPS_COMMANDS_SEND).to(FINISHED_WITH_ERROR).on(ERROR) + builder.externalTransition().from(SEND_REMOVE_COMMANDS).to(FINISHED_WITH_ERROR).on(ERROR) .callMethod(FINISHED_WITH_ERROR_METHOD_NAME); - builder.externalTransition().from(GROUPS_COMMANDS_SEND).to(METERS_COMMANDS_SEND).on(NEXT) - .callMethod("sendMetersCommands"); - builder.internalTransition().within(METERS_COMMANDS_SEND).on(METERS_REMOVED).callMethod("meterRemoved"); - builder.internalTransition().within(METERS_COMMANDS_SEND).on(MISCONFIGURED_METERS_MODIFIED) - .callMethod("meterModified"); + builder.externalTransition().from(SEND_REMOVE_COMMANDS).to(SEND_MODIFY_COMMANDS).on(COMMANDS_PROCESSED) + .callMethod("sendModifyCommands"); - builder.externalTransition().from(METERS_COMMANDS_SEND).to(FINISHED_WITH_ERROR).on(TIMEOUT) + builder.externalTransition().from(SEND_MODIFY_COMMANDS).to(FINISHED_WITH_ERROR).on(TIMEOUT) .callMethod(COMMANDS_PROCESSING_FAILED_BY_TIMEOUT_METHOD_NAME); - builder.externalTransition().from(METERS_COMMANDS_SEND).to(FINISHED_WITH_ERROR).on(ERROR) + builder.externalTransition().from(SEND_MODIFY_COMMANDS).to(FINISHED_WITH_ERROR).on(ERROR) .callMethod(FINISHED_WITH_ERROR_METHOD_NAME); - builder.externalTransition().from(METERS_COMMANDS_SEND).to(RULES_COMMANDS_SEND).on(NEXT) - .callMethod("sendRulesCommands"); - builder.internalTransition().within(RULES_COMMANDS_SEND).on(MISSING_RULES_INSTALLED) - .callMethod("missingRuleInstalled"); - builder.internalTransition().within(RULES_COMMANDS_SEND).on(EXCESS_RULES_REMOVED) - .callMethod("excessRuleRemoved"); - builder.internalTransition().within(RULES_COMMANDS_SEND).on(MISCONFIGURED_RULES_REINSTALLED) - .callMethod("misconfiguredRuleReinstalled"); + builder.externalTransition().from(SEND_MODIFY_COMMANDS).to(SEND_INSTALL_COMMANDS).on(COMMANDS_PROCESSED) + .callMethod("sendInstallCommands"); - builder.externalTransition().from(RULES_COMMANDS_SEND).to(FINISHED_WITH_ERROR).on(TIMEOUT) + builder.externalTransition().from(SEND_INSTALL_COMMANDS).to(FINISHED_WITH_ERROR).on(TIMEOUT) .callMethod(COMMANDS_PROCESSING_FAILED_BY_TIMEOUT_METHOD_NAME); - builder.externalTransition().from(RULES_COMMANDS_SEND).to(FINISHED_WITH_ERROR).on(ERROR) + builder.externalTransition().from(SEND_INSTALL_COMMANDS).to(FINISHED_WITH_ERROR).on(ERROR) .callMethod(FINISHED_WITH_ERROR_METHOD_NAME); - builder.externalTransition().from(RULES_COMMANDS_SEND).to(FINISHED).on(NEXT) + + builder.externalTransition().from(SEND_INSTALL_COMMANDS).to(FINISHED).on(COMMANDS_PROCESSED) .callMethod(FINISHED_METHOD_NAME); builder.defineFinalState(FINISHED); @@ -271,7 +242,12 @@ protected void computeMissingRules(SwitchSyncState from, SwitchSyncState to, if (!installedRulesCookies.isEmpty()) { log.info("Compute install rules (switch={}, key={})", switchId, key); try { - missingRules = commandBuilder.buildCommandsToSyncMissingRules(switchId, installedRulesCookies); + List missingRules = validationResult.getExpectedEntries().stream() + .filter(entry -> entry instanceof FlowSpeakerData) + .map(entry -> (FlowSpeakerData) entry) + .filter(flowEntry -> installedRulesCookies.contains(flowEntry.getCookie().getValue())) + .collect(Collectors.toList()); + toInstall.addAll(missingRules); } catch (Exception e) { sendException(e); } @@ -280,11 +256,20 @@ protected void computeMissingRules(SwitchSyncState from, SwitchSyncState to, protected void computeMisconfiguredRules(SwitchSyncState from, SwitchSyncState to, SwitchSyncEvent event, Object context) { - List reinstallRules = getReinstallDefaultRules(); - if (!reinstallRules.isEmpty()) { - log.info("Compute reinstall default rules (switch={}, key={})", switchId, key); + reinstalledRulesCookies = new ArrayList<>(validationResult.getValidateRulesResult().getMisconfiguredRules()); + if (!reinstalledRulesCookies.isEmpty()) { + log.info("Compute reinstall rules (switch={}, key={})", switchId, key); try { - misconfiguredRules = commandBuilder.buildCommandsToReinstallRules(switchId, reinstallRules); + List misconfiguredRulesToRemove = reinstalledRulesCookies.stream() + .map(this::findActualFlowByCookie) + .collect(Collectors.toList()); + toRemove.addAll(misconfiguredRulesToRemove); + List misconfiguredRulesToInstall = validationResult.getExpectedEntries().stream() + .filter(entry -> entry instanceof FlowSpeakerData) + .map(entry -> (FlowSpeakerData) entry) + .filter(flowEntry -> reinstalledRulesCookies.contains(flowEntry.getCookie().getValue())) + .collect(Collectors.toList()); + toInstall.addAll(misconfiguredRulesToInstall); } catch (Exception e) { sendException(e); } @@ -298,8 +283,10 @@ protected void computeExcessRules(SwitchSyncState from, SwitchSyncState to, if (request.isRemoveExcess() && !removedFlowRulesCookies.isEmpty()) { log.info("Compute remove rules (switch={}, key={})", switchId, key); try { - excessRules = commandBuilder.buildCommandsToRemoveExcessRules( - switchId, validationResult.getFlowEntries(), removedFlowRulesCookies); + List excessRules = removedFlowRulesCookies.stream() + .map(this::findActualFlowByCookie) + .collect(Collectors.toList()); + toRemove.addAll(excessRules); } catch (Exception e) { sendException(e); } @@ -311,12 +298,16 @@ protected void computeExcessMeters(SwitchSyncState from, SwitchSyncState to, if (request.isRemoveExcess() && validationResult.isProcessMeters()) { ValidateMetersResult validateMetersResult = validationResult.getValidateMetersResult(); - if (!validateMetersResult.getExcessMeters().isEmpty()) { + Set excessMeters = validateMetersResult.getExcessMeters().stream() + .map(MeterInfoEntry::getMeterId) + .collect(Collectors.toSet()); + if (!excessMeters.isEmpty()) { log.info("Compute remove meters (switch={}, key={})", switchId, key); try { - excessMeters = validateMetersResult.getExcessMeters().stream() - .map(MeterInfoEntry::getMeterId) + List excessMeterCommands = excessMeters.stream() + .map(this::findActualMeterById) .collect(Collectors.toList()); + toRemove.addAll(excessMeterCommands); } catch (Exception e) { sendException(e); } @@ -325,155 +316,89 @@ protected void computeExcessMeters(SwitchSyncState from, SwitchSyncState to, } protected void computeMisconfiguredMeters(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { + SwitchSyncEvent event, Object context) { if (!validationResult.isProcessMeters()) { return; } - List modifyDefaultMeters = getModifyDefaultMeters(); - List modifyFlowMeters = getModifyFlowMeters(); - if (!modifyDefaultMeters.isEmpty() || !modifyFlowMeters.isEmpty()) { + Set misconfiguredMeters = validationResult.getValidateMetersResult().getMisconfiguredMeters().stream() + .map(MeterInfoEntry::getMeterId) + .collect(Collectors.toSet()); + if (!misconfiguredMeters.isEmpty()) { log.info("Compute modify meters (switch={}, key={})", switchId, key); try { - misconfiguredMeters = commandBuilder.buildCommandsToModifyMisconfiguredMeters( - switchId, modifyDefaultMeters, modifyFlowMeters); + List misconfiguredMeterCommands = misconfiguredMeters.stream() + .map(this::findExpectedMeterById) + .collect(Collectors.toList()); + toModify.addAll(misconfiguredMeterCommands); } catch (Exception e) { sendException(e); } } } - protected void sendRulesCommands(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - if (missingRules.isEmpty() && excessRules.isEmpty() && misconfiguredRules.isEmpty()) { - log.info("Nothing to do with rules (switch={}, key={})", switchId, key); - fire(NEXT); + protected void computeMissingMeters(SwitchSyncState from, SwitchSyncState to, + SwitchSyncEvent event, Object context) { + if (!validationResult.isProcessMeters()) { return; } - - if (!missingRules.isEmpty()) { - log.info("Request to install switch rules has been sent (switch={}, key={})", switchId, key); - missingRulesPendingResponsesCount = missingRules.size(); - - for (BaseFlow command : missingRules) { - carrier.sendCommandToSpeaker(key, new InstallFlowForSwitchManagerRequest(command)); - } - } - - if (!excessRules.isEmpty()) { - log.info("Request to remove switch rules has been sent (switch={}, key={})", switchId, key); - excessRulesPendingResponsesCount = excessRules.size(); - - for (RemoveFlow command : excessRules) { - carrier.sendCommandToSpeaker(key, new RemoveFlowForSwitchManagerRequest(switchId, command)); - } - } - - if (!misconfiguredRules.isEmpty()) { - log.info("Request to reinstall default switch rules has been sent (switch={}, key={})", switchId, key); - reinstallDefaultRulesPendingResponsesCount = misconfiguredRules.size(); - - for (ReinstallDefaultFlowForSwitchManagerRequest command : misconfiguredRules) { - carrier.sendCommandToSpeaker(key, command); - } - } - - continueIfRulesSynchronized(); - } - - private List getReinstallDefaultRules() { - ValidateRulesResult validateRulesResult = validationResult.getValidateRulesResult(); - return validateRulesResult.getMisconfiguredRules().stream() - .filter(Cookie::isDefaultRule) - .collect(Collectors.toList()); - } - - List getModifyDefaultMeters() { - ValidateRulesResult validateRulesResult = validationResult.getValidateRulesResult(); - return validationResult.getValidateMetersResult().getMisconfiguredMeters().stream() - .filter(meter -> MeterId.isMeterIdOfDefaultRule(meter.getMeterId())) - .filter(meter -> !validateRulesResult.getMissingRules().contains(meter.getCookie())) - .filter(meter -> !validateRulesResult.getMisconfiguredRules().contains(meter.getCookie())) + Set missingMeters = validationResult.getValidateMetersResult().getMissingMeters().stream() .map(MeterInfoEntry::getMeterId) - .collect(Collectors.toList()); - } - - List getModifyFlowMeters() { - ValidateRulesResult validateRulesResult = validationResult.getValidateRulesResult(); - return validationResult.getValidateMetersResult().getMisconfiguredMeters().stream() - .filter(meter -> MeterId.isMeterIdOfFlowRule(meter.getMeterId())) - .collect(Collectors.toList()); - } - - protected void sendMetersCommands(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - if (excessMeters.isEmpty() && misconfiguredMeters.isEmpty()) { - log.info("Nothing to do with meters (switch={}, key={})", switchId, key); - fire(NEXT); - return; - } - if (!excessMeters.isEmpty()) { - log.info("Request to remove switch meters has been sent (switch={}, key={})", switchId, key); - excessMetersPendingResponsesCount = excessMeters.size(); - - for (Long meterId : excessMeters) { - carrier.sendCommandToSpeaker(key, new DeleterMeterForSwitchManagerRequest(switchId, meterId)); - } - } - if (!misconfiguredMeters.isEmpty()) { - log.info("Request to modify switch meters has been sent (switch={}, key={})", switchId, key); - misconfiguredMetersPendingResponsesCount = misconfiguredMeters.size(); - - for (ModifyDefaultMeterForSwitchManagerRequest request : misconfiguredMeters) { - carrier.sendCommandToSpeaker(key, request); + .collect(Collectors.toSet()); + if (!missingMeters.isEmpty()) { + log.info("Compute missing meters (switch={}, key={})", switchId, key); + try { + List missingMeterCommands = missingMeters.stream() + .map(this::findExpectedMeterById) + .collect(Collectors.toList()); + toInstall.addAll(missingMeterCommands); + } catch (Exception e) { + sendException(e); } } } - protected void missingRuleInstalled(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - log.info("Switch rule installed (switch={}, key={})", switchId, key); - missingRulesPendingResponsesCount--; - continueIfRulesSynchronized(); + private FlowSpeakerData findActualFlowByCookie(long cookie) { + return validationResult.getActualFlows().stream() + .filter(flowSpeakerData -> flowSpeakerData.getCookie().getValue() == cookie) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + format("Actual rule with cookie %s not found", cookie))); } - protected void excessRuleRemoved(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - log.info("Switch rule removed (switch={}, key={})", switchId, key); - excessRulesPendingResponsesCount--; - continueIfRulesSynchronized(); + private MeterSpeakerData findActualMeterById(long meterId) { + return validationResult.getActualMeters().stream() + .filter(meterSpeakerData -> meterSpeakerData.getMeterId().getValue() == meterId) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + format("Actual meter with id %s not found", meterId))); } - protected void misconfiguredRuleReinstalled(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - log.info("Default switch rule reinstalled (switch={}, key={})", switchId, key); - FlowReinstallResponse response = (FlowReinstallResponse) context; - - Long removedRule = response.getRemovedRule(); - if (removedRule != null && !removedDefaultRulesCookies.contains(removedRule)) { - removedDefaultRulesCookies.add(removedRule); - } - - Long installedRule = response.getInstalledRule(); - if (installedRule != null && !installedRulesCookies.contains(installedRule)) { - installedRulesCookies.add(installedRule); - } - - reinstallDefaultRulesPendingResponsesCount--; - continueIfRulesSynchronized(); + private MeterSpeakerData findExpectedMeterById(long meterId) { + return validationResult.getExpectedEntries().stream() + .filter(entry -> entry instanceof MeterSpeakerData) + .map(entry -> (MeterSpeakerData) entry) + .filter(meterSpeakerData -> meterSpeakerData.getMeterId().getValue() == meterId) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + format("Expected meter with id %s not found", meterId))); } - protected void meterRemoved(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - log.info("Switch meter removed (switch={}, key={})", switchId, key); - excessMetersPendingResponsesCount--; - continueIfMetersCommandsDone(); + private GroupSpeakerData findActualGroupById(long groupId) { + return validationResult.getActualGroups().stream() + .filter(groupSpeakerData -> groupSpeakerData.getGroupId().getValue() == groupId) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + format("Actual group with id %s not found", groupId))); } - protected void meterModified(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - log.info("Switch meter modified (switch={}, key={})", switchId, key); - misconfiguredMetersPendingResponsesCount--; - continueIfMetersCommandsDone(); + private GroupSpeakerData findExpectedGroupById(long groupId) { + return validationResult.getExpectedEntries().stream() + .filter(entry -> entry instanceof GroupSpeakerData) + .map(entry -> (GroupSpeakerData) entry) + .filter(groupSpeakerData -> groupSpeakerData.getGroupId().getValue() == groupId) + .findFirst() + .orElseThrow(() -> new IllegalStateException( + format("Expected group with id %s not found", groupId))); } protected void computeLogicalPortsCommands(SwitchSyncState from, SwitchSyncState to, @@ -508,8 +433,8 @@ protected void computeLogicalPortsCommands(SwitchSyncState from, SwitchSyncState } } - protected void sendLogicalPortsCommandsCommands(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { + protected void sendLogicalPortsCommands(SwitchSyncState from, SwitchSyncState to, + SwitchSyncEvent event, Object context) { if (missingLogicalPorts.isEmpty() && excessLogicalPorts.isEmpty()) { log.info("Nothing to do with logical ports (switch={}, key={})", switchId, key); fire(NEXT); @@ -560,9 +485,12 @@ protected void computeGroupMirrorConfigs(SwitchSyncState from, SwitchSyncState t .collect(Collectors.toList()); if (!missingGroupIds.isEmpty()) { - log.info("Compute mirror configs for install groups (switch={}, key={})", switchId, key); + log.info("Compute missing groups (switch={}, key={})", switchId, key); try { - missingGroups = commandBuilder.buildGroupInstallContexts(switchId, missingGroupIds); + List missingGroups = missingGroupIds.stream() + .map(this::findExpectedGroupById) + .collect(Collectors.toList()); + toInstall.addAll(missingGroups); } catch (Exception e) { sendException(e); } @@ -573,101 +501,91 @@ protected void computeGroupMirrorConfigs(SwitchSyncState from, SwitchSyncState t .map(GroupInfoEntry::getGroupId) .collect(Collectors.toList()); if (!misconfiguredGroupIds.isEmpty()) { - log.info("Compute mirror configs for modify groups (switch={}, key={})", switchId, key); + log.info("Compute misconfigured groups (switch={}, key={})", switchId, key); try { - misconfiguredGroups = commandBuilder.buildGroupInstallContexts(switchId, misconfiguredGroupIds); + List misconfiguredGroups = misconfiguredGroupIds.stream() + .map(this::findExpectedGroupById) + .collect(Collectors.toList()); + toModify.addAll(misconfiguredGroups); } catch (Exception e) { sendException(e); } } - excessGroups = validationResult.getValidateGroupsResult().getExcessGroups().stream() + Set excessGroups = validationResult.getValidateGroupsResult().getExcessGroups().stream() .map(GroupInfoEntry::getGroupId) - .collect(Collectors.toList()); - } - - protected void sendGroupsCommands(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - if (missingGroups.isEmpty() && misconfiguredGroups.isEmpty() && excessGroups.isEmpty()) { - log.info("Nothing to do with groups (switch={}, key={})", switchId, key); - fire(NEXT); - return; - } - - if (!missingGroups.isEmpty()) { - log.info("Request to install switch groups has been sent (switch={}, key={})", switchId, key); - missingGroupsPendingResponsesCount = missingGroups.size(); - - for (GroupInstallContext groupContext : missingGroups) { - carrier.sendCommandToSpeaker(key, new InstallGroupRequest(switchId, groupContext.getMirrorConfig(), - groupContext.getEncapsulation(), groupContext.getEgressSwitchId())); - } - } - - if (!misconfiguredGroups.isEmpty()) { - log.info("Request to modify switch groups has been sent (switch={}, key={})", switchId, key); - misconfiguredGroupsPendingResponsesCount = misconfiguredGroups.size(); - - for (GroupInstallContext groupContext : misconfiguredGroups) { - carrier.sendCommandToSpeaker(key, new ModifyGroupRequest(switchId, groupContext.getMirrorConfig(), - groupContext.getEncapsulation(), groupContext.getEgressSwitchId())); - } - } - + .collect(Collectors.toSet()); if (!excessGroups.isEmpty()) { - log.info("Request to remove switch groups has been sent (switch={}, key={})", switchId, key); - excessGroupsPendingResponsesCount = excessGroups.size(); - - for (Integer groupId : excessGroups) { - carrier.sendCommandToSpeaker(key, new DeleteGroupRequest(switchId, new GroupId(groupId))); + log.info("Compute excess groups (switch={}, key={})", switchId, key); + try { + List excessGroupCommands = excessGroups.stream() + .map(this::findActualGroupById) + .collect(Collectors.toList()); + toRemove.addAll(excessGroupCommands); + } catch (Exception e) { + sendException(e); } } - - continueIfGroupsSynchronized(); } - protected void groupsInstalled(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - log.info("Switch group installed (switch={}, key={})", switchId, key); - missingGroupsPendingResponsesCount--; - continueIfGroupsSynchronized(); + protected void sendRemoveCommands(SwitchSyncState from, SwitchSyncState to, + SwitchSyncEvent event, Object context) { + if (toRemove.isEmpty()) { + log.info("No need to process remove commands (switch={}, key={})", switchId, key); + fire(COMMANDS_PROCESSED); + return; + } + List commands = cleanupDependenciesAndBuildCommands(toRemove); + log.info("Remove commands has been sent (switch={}, key={})", switchId, key); + carrier.sendOfCommandsToSpeaker(key, commands, OfCommandAction.DELETE, switchId); } - protected void groupsModified(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - log.info("Switch group modified (switch={}, key={})", switchId, key); - misconfiguredGroupsPendingResponsesCount--; - continueIfGroupsSynchronized(); - } + protected void sendModifyCommands(SwitchSyncState from, SwitchSyncState to, + SwitchSyncEvent event, Object context) { + if (toModify.isEmpty()) { + log.info("No need to process modify commands (switch={}, key={})", switchId, key); + fire(COMMANDS_PROCESSED); + return; + } + List commands = cleanupDependenciesAndBuildCommands(toModify); - protected void groupsRemoved(SwitchSyncState from, SwitchSyncState to, - SwitchSyncEvent event, Object context) { - log.info("Switch group removed (switch={}, key={})", switchId, key); - excessGroupsPendingResponsesCount--; - continueIfGroupsSynchronized(); + log.info("Modify commands has been sent (switch={}, key={})", switchId, key); + carrier.sendOfCommandsToSpeaker(key, commands, OfCommandAction.MODIFY, switchId); } - private void continueIfRulesSynchronized() { - if (missingRulesPendingResponsesCount == 0 - && excessRulesPendingResponsesCount == 0 - && reinstallDefaultRulesPendingResponsesCount == 0) { - fire(NEXT); + protected void sendInstallCommands(SwitchSyncState from, SwitchSyncState to, + SwitchSyncEvent event, Object context) { + if (toInstall.isEmpty()) { + log.info("No need to process install commands (switch={}, key={})", switchId, key); + fire(COMMANDS_PROCESSED); + return; } + List commands = cleanupDependenciesAndBuildCommands(toInstall); + + log.info("Install commands has been sent (switch={}, key={})", switchId, key); + carrier.sendOfCommandsToSpeaker(key, commands, OfCommandAction.INSTALL, switchId); } - private void continueIfMetersCommandsDone() { - if (excessMetersPendingResponsesCount == 0 && misconfiguredMetersPendingResponsesCount == 0) { - fire(NEXT); - } + private List cleanupDependenciesAndBuildCommands(List source) { + Set ids = source.stream() + .map(SpeakerData::getUuid) + .collect(Collectors.toSet()); + source.forEach(speakerData -> speakerData.getDependsOn().retainAll(ids)); + return source.stream() + .map(this::toCommand) + .collect(Collectors.toList()); } - private void continueIfGroupsSynchronized() { - if (missingGroupsPendingResponsesCount == 0 - && misconfiguredGroupsPendingResponsesCount == 0 - && excessGroupsPendingResponsesCount == 0) { - fire(NEXT); + private OfCommand toCommand(SpeakerData speakerData) { + if (speakerData instanceof FlowSpeakerData) { + return new FlowCommand((FlowSpeakerData) speakerData); + } else if (speakerData instanceof MeterSpeakerData) { + return new MeterCommand((MeterSpeakerData) speakerData); + } else if (speakerData instanceof GroupSpeakerData) { + return new GroupCommand((GroupSpeakerData) speakerData); } + throw new IllegalStateException(format("Unknown speaker data type %s", speakerData)); } private void continueIfLogicalPortsSynchronized() { @@ -699,9 +617,9 @@ protected void finished(SwitchSyncState from, SwitchSyncState to, new ArrayList<>(validateRulesResult.getMisconfiguredRules()), new ArrayList<>(validateRulesResult.getProperRules()), new ArrayList<>(validateRulesResult.getExcessRules()), - installedRulesCookies, - request.isRemoveExcess() ? union(removedFlowRulesCookies, removedDefaultRulesCookies) - : removedDefaultRulesCookies); + union(installedRulesCookies, reinstalledRulesCookies), + request.isRemoveExcess() ? union(removedFlowRulesCookies, reinstalledRulesCookies) + : reinstalledRulesCookies); MetersSyncEntry metersEntry = null; if (validationResult.isProcessMeters()) { @@ -713,10 +631,9 @@ protected void finished(SwitchSyncState from, SwitchSyncState to, request.isRemoveExcess() ? validateMetersResult.getExcessMeters() : emptyList()); } - List installedGroupsIds = missingGroups.stream().map(GroupInstallContext::getMirrorConfig) - .map(MirrorConfig::getGroupId).map(GroupId::intValue).collect(Collectors.toList()); - List modifiedGroupsIds = misconfiguredGroups.stream().map(GroupInstallContext::getMirrorConfig) - .map(MirrorConfig::getGroupId).map(GroupId::intValue).collect(Collectors.toList()); + List installedGroupsIds = extractGroupIds(toInstall); + List modifiedGroupsIds = extractGroupIds(toModify); + List removedGroupsIds = extractGroupIds(toRemove); GroupSyncEntry groupEntry = GroupSyncEntry.builder() .proper(validateGroupsResult.getProperGroups()) @@ -725,7 +642,7 @@ protected void finished(SwitchSyncState from, SwitchSyncState to, .excess(validateGroupsResult.getExcessGroups()) .installed(mapToGroupEntryList(installedGroupsIds, validateGroupsResult.getMissingGroups())) .modified(mapToGroupEntryList(modifiedGroupsIds, validateGroupsResult.getMisconfiguredGroups())) - .removed(mapToGroupEntryList(excessGroups, validateGroupsResult.getExcessGroups())) + .removed(mapToGroupEntryList(removedGroupsIds, validateGroupsResult.getExcessGroups())) .build(); LogicalPortsSyncEntry logicalPortsEntry = LogicalPortsSyncEntry.builder() @@ -735,6 +652,7 @@ protected void finished(SwitchSyncState from, SwitchSyncState to, .missing(validateLogicalPortsResult.getMissingLogicalPorts()) .installed(validateLogicalPortsResult.getMissingLogicalPorts()) .removed(validateLogicalPortsResult.getExcessLogicalPorts()) + .error(validateLogicalPortsResult.getErrorMessage()) .build(); SwitchSyncResponse response = new SwitchSyncResponse(switchId, rulesEntry, metersEntry, groupEntry, @@ -745,6 +663,14 @@ protected void finished(SwitchSyncState from, SwitchSyncState to, carrier.response(key, message); } + private List extractGroupIds(List source) { + return source.stream() + .filter(speakerData -> speakerData instanceof GroupSpeakerData) + .map(speakerData -> (GroupSpeakerData) speakerData) + .map(groupSpeakerData -> (int) groupSpeakerData.getGroupId().getValue()) + .collect(Collectors.toList()); + } + private List mapToGroupEntryList(List groupIds, List groupEntries) { return groupEntries.stream() @@ -764,6 +690,7 @@ protected void finishedWithError(SwitchSyncState from, SwitchSyncState to, } private void sendException(Exception e) { + log.error("Error in switch {} sync", switchId, e); ErrorData errorData = new SwitchSyncErrorData(switchId, ErrorType.INTERNAL_ERROR, e.getMessage(), "Error in SwitchSyncFsm"); fire(ERROR, errorData); @@ -776,11 +703,12 @@ public enum SwitchSyncState { COMPUTE_EXCESS_RULES, COMPUTE_EXCESS_METERS, COMPUTE_MISCONFIGURED_METERS, + COMPUTE_MISSING_METERS, COMPUTE_GROUP_MIRROR_CONFIGS, COMPUTE_LOGICAL_PORTS_COMMANDS, - RULES_COMMANDS_SEND, - METERS_COMMANDS_SEND, - GROUPS_COMMANDS_SEND, + SEND_REMOVE_COMMANDS, + SEND_MODIFY_COMMANDS, + SEND_INSTALL_COMMANDS, LOGICAL_PORTS_COMMANDS_SEND, FINISHED_WITH_ERROR, FINISHED @@ -788,14 +716,7 @@ public enum SwitchSyncState { public enum SwitchSyncEvent { NEXT, - MISSING_RULES_INSTALLED, - EXCESS_RULES_REMOVED, - MISCONFIGURED_RULES_REINSTALLED, - METERS_REMOVED, - MISCONFIGURED_METERS_MODIFIED, - GROUPS_INSTALLED, - GROUPS_MODIFIED, - GROUPS_REMOVED, + COMMANDS_PROCESSED, LOGICAL_PORT_INSTALLED, LOGICAL_PORT_REMOVED, TIMEOUT, diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/fsm/SwitchValidateFsm.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/fsm/SwitchValidateFsm.java index 9761e53255d..ece8073c8e7 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/fsm/SwitchValidateFsm.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/fsm/SwitchValidateFsm.java @@ -17,6 +17,7 @@ import static org.openkilda.model.SwitchFeature.LAG; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchValidateFsm.SwitchValidateEvent.ERROR; +import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchValidateFsm.SwitchValidateEvent.ERROR_RECEIVED; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchValidateFsm.SwitchValidateEvent.EXPECTED_ENTITIES_BUILT; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchValidateFsm.SwitchValidateEvent.GROUPS_RECEIVED; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchValidateFsm.SwitchValidateEvent.LOGICAL_PORTS_RECEIVED; @@ -29,7 +30,10 @@ import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchValidateFsm.SwitchValidateState.FINISHED_WITH_ERROR; import static org.openkilda.wfm.topology.switchmanager.fsm.SwitchValidateFsm.SwitchValidateState.VALIDATE; +import org.openkilda.messaging.MessageCookie; +import org.openkilda.messaging.command.CommandData; import org.openkilda.messaging.command.grpc.DumpLogicalPortsRequest; +import org.openkilda.messaging.command.grpc.GrpcBaseRequest; import org.openkilda.messaging.command.switches.DumpGroupsForSwitchManagerRequest; import org.openkilda.messaging.command.switches.DumpMetersForSwitchManagerRequest; import org.openkilda.messaging.command.switches.DumpRulesForSwitchManagerRequest; @@ -65,16 +69,20 @@ import org.openkilda.wfm.topology.switchmanager.service.SwitchManagerCarrier; import org.openkilda.wfm.topology.switchmanager.service.ValidationService; +import com.fasterxml.uuid.Generators; +import com.fasterxml.uuid.NoArgGenerator; import lombok.Builder; -import lombok.Value; +import lombok.Data; import lombok.extern.slf4j.Slf4j; import org.squirrelframework.foundation.fsm.StateMachineBuilder; import org.squirrelframework.foundation.fsm.StateMachineBuilderFactory; import org.squirrelframework.foundation.fsm.StateMachineStatus; import org.squirrelframework.foundation.fsm.impl.AbstractStateMachine; +import java.util.HashMap; import java.util.HashSet; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.stream.Collectors; @@ -89,6 +97,8 @@ public class SwitchValidateFsm extends AbstractStateMachine< private final SwitchValidateRequest request; private final SwitchManagerCarrier carrier; private final ValidationService validationService; + private static final Map requestsTable = new HashMap<>(); + private final NoArgGenerator requestIdGenerator = Generators.timeBasedGenerator(); private SwitchValidationContext validationContext; private final Set pendingRequests = new HashSet<>(); @@ -143,9 +153,12 @@ SwitchValidateEvent, SwitchValidateContext> builder() { .on(METERS_UNSUPPORTED).callMethod("metersUnsupported"); builder.internalTransition().within(SwitchValidateState.COLLECT_DATA) .on(EXPECTED_ENTITIES_BUILT).callMethod("expectedEntitiesBuild"); + builder.internalTransition().within(SwitchValidateState.COLLECT_DATA) + .on(ERROR_RECEIVED).callMethod("errorWhileCollectingData"); builder.externalTransition().from(SwitchValidateState.COLLECT_DATA).to(VALIDATE).on(READY); builder.externalTransition().from(SwitchValidateState.COLLECT_DATA).to(FINISHED_WITH_ERROR).on(ERROR); + // VALIDATE builder.externalTransition().from(VALIDATE).to(FINISHED).on(NEXT); builder.externalTransition().from(VALIDATE).to(FINISHED_WITH_ERROR).on(ERROR); @@ -269,6 +282,35 @@ protected void expectedEntitiesBuild(SwitchValidateState from, SwitchValidateSta fireReadyIfAllResourcesReceived(); } + protected void errorWhileCollectingData(SwitchValidateState from, SwitchValidateState to, + SwitchValidateEvent event, SwitchValidateContext context) { + log.info("Caught error while collecting data, checking source (switch={}, key={}", getSwitchId(), key); + MessageCookie sourceCookie = context.getRequestCookie(); + if (sourceCookie == null) { + log.warn("Request cookie is null, aborting"); + fire(ERROR, context); + return; + } + CommandData request = requestsTable.remove(sourceCookie.getValue()); + if (request == null) { + log.warn("Request got by cookie is null, aborting (cookie={})", sourceCookie); + fire(ERROR, context); + return; + } + SwitchManagerException error = context.getError(); + if (error != null && request instanceof GrpcBaseRequest) { + log.warn("Got error from GRPC request (cookie={})", sourceCookie); + pendingRequests.remove(ExternalResources.ACTUAL_LOGICAL_PORTS); + validationContext = validationContext.toBuilder() + .validateLogicalPortResult(ValidateLogicalPortsResult.newErrorMessage(error.getMessage())) + .build(); + fireReadyIfAllResourcesReceived(); + } else { + log.warn("Got error from floodlight request (cookie={})", sourceCookie); + fire(ERROR, context); + } + } + protected void validateEnter(SwitchValidateState from, SwitchValidateState to, SwitchValidateEvent event, SwitchValidateContext context) { List expectedEntities = validationContext.getExpectedSwitchEntities(); @@ -289,6 +331,10 @@ protected void finishedEnter(SwitchValidateState from, SwitchValidateState to, .collect(Collectors.toList()); ValidationResult results = new ValidationResult(flowEntries, validationContext.getMetersValidationReport() != null, + validationContext.getExpectedSwitchEntities(), + validationContext.getActualOfFlows(), + validationContext.getActualMeters(), + validationContext.getActualGroupEntries(), validationContext.getOfFlowsValidationReport(), validationContext.getMetersValidationReport(), validationContext.getValidateGroupsResult(), validationContext.getValidateLogicalPortResult()); carrier.runSwitchSync(key, request, results); @@ -339,33 +385,45 @@ private void requestSwitchExpectedEntities() { private void requestSwitchOfFlows() { SwitchId switchId = getSwitchId(); log.info("Sending requests to get switch OF-flows (switch={}, key={})", switchId, key); + String uuid = String.valueOf(requestIdGenerator.generate()); + CommandData request = new DumpRulesForSwitchManagerRequest(switchId); - carrier.sendCommandToSpeaker(key, new DumpRulesForSwitchManagerRequest(switchId)); + carrier.sendCommandToSpeaker(uuid, request); pendingRequests.add(ExternalResources.ACTUAL_OF_FLOWS); + requestsTable.put(uuid, request); } private void requestSwitchOfGroups() { SwitchId switchId = getSwitchId(); log.info("Sending requests to get switch OF-groups (switch={}, key={})", switchId, key); + String uuid = String.valueOf(requestIdGenerator.generate()); + CommandData request = new DumpGroupsForSwitchManagerRequest(switchId); - carrier.sendCommandToSpeaker(key, new DumpGroupsForSwitchManagerRequest(switchId)); + carrier.sendCommandToSpeaker(uuid, request); pendingRequests.add(ExternalResources.ACTUAL_OF_GROUPS); + requestsTable.put(uuid, request); } private void requestLogicalPorts(String ipAddress) { SwitchId switchId = getSwitchId(); log.info("Sending request to get switch logical ports. IP {} (switch={}, key={})", ipAddress, switchId, key); + String uuid = String.valueOf(requestIdGenerator.generate()); + CommandData request = new DumpLogicalPortsRequest(ipAddress); - carrier.sendCommandToSpeaker(key, new DumpLogicalPortsRequest(ipAddress)); + carrier.sendCommandToSpeaker(uuid, request); pendingRequests.add(ExternalResources.ACTUAL_LOGICAL_PORTS); + requestsTable.put(uuid, request); } private void requestSwitchMeters() { SwitchId switchId = getSwitchId(); log.info("Sending requests to get switch meters (switch={}, key={})", switchId, key); + String uuid = String.valueOf(requestIdGenerator.generate()); + CommandData request = new DumpMetersForSwitchManagerRequest(switchId); - carrier.sendCommandToSpeaker(key, new DumpMetersForSwitchManagerRequest(switchId)); + carrier.sendCommandToSpeaker(uuid, request); pendingRequests.add(ExternalResources.ACTUAL_METERS); + requestsTable.put(uuid, request); } private void validateRules(List expectedRules) { @@ -460,6 +518,7 @@ public enum SwitchValidateEvent { LOGICAL_PORTS_RECEIVED, METERS_UNSUPPORTED, EXPECTED_ENTITIES_BUILT, + ERROR_RECEIVED, ERROR } @@ -471,7 +530,7 @@ private enum ExternalResources { EXPECTED_ENTITIES } - @Value + @Data @Builder public static class SwitchValidateContext { List flowEntries; @@ -480,5 +539,6 @@ public static class SwitchValidateContext { List logicalPortEntries; List expectedEntities; SwitchManagerException error; + MessageCookie requestCookie; } } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/mappers/FlowEntryConverter.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/mappers/FlowEntryConverter.java index 3bb89ee40c0..a5984ea6bb5 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/mappers/FlowEntryConverter.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/mappers/FlowEntryConverter.java @@ -127,6 +127,7 @@ private FlowInstructions convertInstructions(Instructions instructions) { if (instructions.getApplyActions() != null) { instructions.getApplyActions().forEach(action -> addAction(actionsBuilder, action)); } + builder.applyActions(actionsBuilder.build()); return builder.build(); } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/mappers/ValidationMapper.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/mappers/ValidationMapper.java index 4f97d8486d0..567e962ca90 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/mappers/ValidationMapper.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/mappers/ValidationMapper.java @@ -81,5 +81,6 @@ public SwitchValidationResponse toSwitchResponse(SwitchValidationContext validat @Mapping(source = "misconfiguredLogicalPorts", target = "misconfigured") @Mapping(source = "properLogicalPorts", target = "proper") @Mapping(source = "excessLogicalPorts", target = "excess") + @Mapping(source = "errorMessage", target = "error") public abstract LogicalPortsValidationEntry mapReport(ValidateLogicalPortsResult report); } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/model/ValidateLogicalPortsResult.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/model/ValidateLogicalPortsResult.java index 3b059190e57..7c6946f63fb 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/model/ValidateLogicalPortsResult.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/model/ValidateLogicalPortsResult.java @@ -26,11 +26,17 @@ public class ValidateLogicalPortsResult { public static ValidateLogicalPortsResult newEmpty() { return new ValidateLogicalPortsResult( - new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); + new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), ""); + } + + public static ValidateLogicalPortsResult newErrorMessage(String error) { + return new ValidateLogicalPortsResult( + new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), new ArrayList<>(), error); } List properLogicalPorts; List missingLogicalPorts; List excessLogicalPorts; List misconfiguredLogicalPorts; + String errorMessage; } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/model/ValidationResult.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/model/ValidationResult.java index f9e1ffc6a14..d66ad708551 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/model/ValidationResult.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/model/ValidationResult.java @@ -16,6 +16,10 @@ package org.openkilda.wfm.topology.switchmanager.model; import org.openkilda.messaging.info.rule.FlowEntry; +import org.openkilda.rulemanager.FlowSpeakerData; +import org.openkilda.rulemanager.GroupSpeakerData; +import org.openkilda.rulemanager.MeterSpeakerData; +import org.openkilda.rulemanager.SpeakerData; import lombok.Value; @@ -25,6 +29,10 @@ public class ValidationResult { List flowEntries; boolean processMeters; + List expectedEntries; + List actualFlows; + List actualMeters; + List actualGroups; ValidateRulesResult validateRulesResult; ValidateMetersResult validateMetersResult; diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/CommandBuilder.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/CommandBuilder.java index acc280dc48d..35c0c82531e 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/CommandBuilder.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/CommandBuilder.java @@ -15,36 +15,15 @@ package org.openkilda.wfm.topology.switchmanager.service; -import org.openkilda.messaging.command.flow.BaseFlow; -import org.openkilda.messaging.command.flow.ModifyDefaultMeterForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.ReinstallDefaultFlowForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.RemoveFlow; import org.openkilda.messaging.command.grpc.CreateOrUpdateLogicalPortRequest; import org.openkilda.messaging.command.grpc.DeleteLogicalPortRequest; -import org.openkilda.messaging.info.rule.FlowEntry; import org.openkilda.messaging.info.switches.LogicalPortInfoEntry; -import org.openkilda.messaging.info.switches.MeterInfoEntry; import org.openkilda.model.SwitchId; -import org.openkilda.wfm.topology.switchmanager.model.GroupInstallContext; import java.util.List; public interface CommandBuilder { - List buildCommandsToSyncMissingRules(SwitchId switchId, List switchRules); - - List buildCommandsToRemoveExcessRules(SwitchId switchId, - List flows, - List excessRulesCookies); - - List buildCommandsToReinstallRules( - SwitchId switchId, List reinstallRulesCookies); - - List buildCommandsToModifyMisconfiguredMeters( - SwitchId switchId, List misconfiguredDefaultMeters, List misconfiguredFlowMeters); - - List buildGroupInstallContexts(SwitchId switchId, List groupIds); - List buildLogicalPortInstallCommands( SwitchId switchId, List missingLogicalPorts); diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SpeakerCommandCarrier.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SpeakerCommandCarrier.java index 8619b5fafbe..289dbe319e6 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SpeakerCommandCarrier.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SpeakerCommandCarrier.java @@ -15,6 +15,8 @@ package org.openkilda.wfm.topology.switchmanager.service; +import org.openkilda.floodlight.api.request.SpeakerRequest; +import org.openkilda.floodlight.api.response.SpeakerResponse; import org.openkilda.messaging.Message; import org.openkilda.messaging.command.CommandMessage; import org.openkilda.wfm.error.PipelineException; @@ -23,7 +25,11 @@ public interface SpeakerCommandCarrier { void sendFloodlightCommand(String key, CommandMessage command) throws PipelineException; + void sendFloodlightOfRequest(String key, SpeakerRequest request) throws PipelineException; + void sendGrpcCommand(String key, CommandMessage command) throws PipelineException; void sendResponse(String key, Message response) throws PipelineException; + + void sendSpeakerResponse(String key, SpeakerResponse response) throws PipelineException; } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerCarrier.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerCarrier.java index a25e929314f..7bc13856122 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerCarrier.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerCarrier.java @@ -15,6 +15,7 @@ package org.openkilda.wfm.topology.switchmanager.service; +import org.openkilda.floodlight.api.request.rulemanager.OfCommand; import org.openkilda.messaging.Message; import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.command.CommandData; @@ -22,10 +23,13 @@ import org.openkilda.messaging.error.ErrorType; import org.openkilda.messaging.info.InfoData; import org.openkilda.model.SwitchId; +import org.openkilda.wfm.topology.switchmanager.bolt.SwitchManagerHub.OfCommandAction; import org.openkilda.wfm.topology.switchmanager.model.ValidationResult; import lombok.NonNull; +import java.util.List; + public interface SwitchManagerCarrier { MessageCookie newDispatchRoute(String requestKey); @@ -37,6 +41,11 @@ public interface SwitchManagerCarrier { void runHeavyOperation(SwitchId switchId, @NonNull MessageCookie messageCookie); + void sendOfCommandsToSpeaker(String key, List commands, OfCommandAction action, SwitchId switchId); + + void sendOfCommandsToSpeaker(List commands, OfCommandAction action, SwitchId switchId, + @NonNull MessageCookie cookie); + void response(String key, Message message); void response(String key, InfoData payload); diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerCarrierCookieDecorator.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerCarrierCookieDecorator.java index 98b64c2d914..3be716c93c1 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerCarrierCookieDecorator.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerCarrierCookieDecorator.java @@ -15,6 +15,7 @@ package org.openkilda.wfm.topology.switchmanager.service; +import org.openkilda.floodlight.api.request.rulemanager.OfCommand; import org.openkilda.messaging.Message; import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.command.CommandData; @@ -22,10 +23,13 @@ import org.openkilda.messaging.error.ErrorType; import org.openkilda.messaging.info.InfoData; import org.openkilda.model.SwitchId; +import org.openkilda.wfm.topology.switchmanager.bolt.SwitchManagerHub.OfCommandAction; import org.openkilda.wfm.topology.switchmanager.model.ValidationResult; import lombok.NonNull; +import java.util.List; + public class SwitchManagerCarrierCookieDecorator implements SwitchManagerCarrier { private final SwitchManagerCarrier target; @@ -61,6 +65,18 @@ public void runHeavyOperation(SwitchId switchId, @NonNull MessageCookie cookie) target.runHeavyOperation(switchId, new MessageCookie(dispatchRoute, cookie)); } + @Override + public void sendOfCommandsToSpeaker(String key, List commands, OfCommandAction action, + SwitchId switchId) { + sendOfCommandsToSpeaker(commands, action, switchId, new MessageCookie(key)); + } + + @Override + public void sendOfCommandsToSpeaker(List commands, OfCommandAction action, SwitchId switchId, + @NonNull MessageCookie cookie) { + target.sendOfCommandsToSpeaker(commands, action, switchId, new MessageCookie(dispatchRoute, cookie)); + } + @Override public void response(String key, Message message) { target.response(key, message); diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerHubService.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerHubService.java index 1c2bf20a26c..18f46b9cd8e 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerHubService.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchManagerHubService.java @@ -15,6 +15,7 @@ package org.openkilda.wfm.topology.switchmanager.service; +import org.openkilda.floodlight.api.response.SpeakerResponse; import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.error.ErrorData; import org.openkilda.messaging.info.InfoData; @@ -35,6 +36,9 @@ public interface SwitchManagerHubService { void dispatchWorkerMessage(InfoData payload, MessageCookie cookie) throws UnexpectedInputException, MessageDispatchException; + default void dispatchWorkerMessage(SpeakerResponse payload, MessageCookie cookie) + throws UnexpectedInputException, MessageDispatchException {} + void dispatchErrorMessage(ErrorData payload, MessageCookie cookie) throws MessageDispatchException; default void dispatchHeavyOperationMessage(InfoData payload, MessageCookie cookie) diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchSyncService.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchSyncService.java index 476bec3fb22..fc96e64b788 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchSyncService.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchSyncService.java @@ -15,24 +15,18 @@ package org.openkilda.wfm.topology.switchmanager.service; +import org.openkilda.floodlight.api.response.SpeakerResponse; +import org.openkilda.floodlight.api.response.rulemanager.SpeakerCommandResponse; import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.command.switches.SwitchValidateRequest; import org.openkilda.messaging.error.ErrorData; +import org.openkilda.messaging.error.ErrorType; import org.openkilda.messaging.info.InfoData; -import org.openkilda.messaging.info.flow.FlowInstallResponse; -import org.openkilda.messaging.info.flow.FlowReinstallResponse; -import org.openkilda.messaging.info.flow.FlowRemoveResponse; import org.openkilda.messaging.info.grpc.CreateOrUpdateLogicalPortResponse; import org.openkilda.messaging.info.grpc.DeleteLogicalPortResponse; -import org.openkilda.messaging.info.switches.DeleteGroupResponse; -import org.openkilda.messaging.info.switches.DeleteMeterResponse; -import org.openkilda.messaging.info.switches.InstallGroupResponse; -import org.openkilda.messaging.info.switches.ModifyGroupResponse; -import org.openkilda.messaging.info.switches.ModifyMeterResponse; import org.openkilda.persistence.PersistenceManager; import org.openkilda.wfm.error.MessageDispatchException; import org.openkilda.wfm.error.UnexpectedInputException; -import org.openkilda.wfm.share.flow.resources.FlowResourcesConfig; import org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm; import org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncEvent; import org.openkilda.wfm.topology.switchmanager.fsm.SwitchSyncFsm.SwitchSyncState; @@ -65,9 +59,8 @@ public class SwitchSyncService implements SwitchManagerHubService { CommandBuilder commandBuilder; public SwitchSyncService( - SwitchManagerCarrier carrier, PersistenceManager persistenceManager, - FlowResourcesConfig flowResourcesConfig) { - this(carrier, new CommandBuilderImpl(persistenceManager, flowResourcesConfig)); + SwitchManagerCarrier carrier, PersistenceManager persistenceManager) { + this(carrier, new CommandBuilderImpl(persistenceManager)); } @VisibleForTesting @@ -85,23 +78,7 @@ public void timeout(@NonNull MessageCookie cookie) throws MessageDispatchExcepti @Override public void dispatchWorkerMessage(InfoData payload, MessageCookie cookie) throws UnexpectedInputException, MessageDispatchException { - if (payload instanceof FlowInstallResponse) { - fireHandlerEvent(cookie, SwitchSyncEvent.MISSING_RULES_INSTALLED); - } else if (payload instanceof FlowRemoveResponse) { - fireHandlerEvent(cookie, SwitchSyncEvent.EXCESS_RULES_REMOVED); - } else if (payload instanceof FlowReinstallResponse) { - fireHandlerEvent(cookie, SwitchSyncEvent.MISCONFIGURED_RULES_REINSTALLED, payload); - } else if (payload instanceof DeleteMeterResponse) { - fireHandlerEvent(cookie, SwitchSyncEvent.METERS_REMOVED); - } else if (payload instanceof ModifyMeterResponse) { - fireHandlerEvent(cookie, SwitchSyncEvent.MISCONFIGURED_METERS_MODIFIED); - } else if (payload instanceof InstallGroupResponse) { - fireHandlerEvent(cookie, SwitchSyncEvent.GROUPS_INSTALLED); - } else if (payload instanceof ModifyGroupResponse) { - fireHandlerEvent(cookie, SwitchSyncEvent.GROUPS_MODIFIED); - } else if (payload instanceof DeleteGroupResponse) { - fireHandlerEvent(cookie, SwitchSyncEvent.GROUPS_REMOVED); - } else if (payload instanceof CreateOrUpdateLogicalPortResponse) { + if (payload instanceof CreateOrUpdateLogicalPortResponse) { fireHandlerEvent(cookie, SwitchSyncEvent.LOGICAL_PORT_INSTALLED); } else if (payload instanceof DeleteLogicalPortResponse) { fireHandlerEvent(cookie, SwitchSyncEvent.LOGICAL_PORT_REMOVED); @@ -110,6 +87,23 @@ public void dispatchWorkerMessage(InfoData payload, MessageCookie cookie) } } + @Override + public void dispatchWorkerMessage(SpeakerResponse payload, MessageCookie cookie) + throws UnexpectedInputException, MessageDispatchException { + if (payload instanceof SpeakerCommandResponse) { + SpeakerCommandResponse response = (SpeakerCommandResponse) payload; + if (response.isSuccess()) { + fireHandlerEvent(cookie, SwitchSyncEvent.COMMANDS_PROCESSED); + } else { + ErrorData errorData = new ErrorData(ErrorType.INTERNAL_ERROR, "OpenFlow commands failed", + response.getFailedCommandIds().values().toString()); + fireHandlerEvent(cookie, SwitchSyncEvent.ERROR, errorData); + } + } else { + throw new UnexpectedInputException(payload); + } + } + @Override public void dispatchErrorMessage(ErrorData payload, MessageCookie cookie) throws MessageDispatchException { fireHandlerEvent(cookie, SwitchSyncEvent.ERROR, payload); @@ -147,9 +141,9 @@ private void fireHandlerEvent(MessageCookie cookie, SwitchSyncEvent event, Objec // FIXME(surabujin): incorrect FSM usage private void process(SwitchSyncFsm fsm) { final List stopStates = Arrays.asList( - SwitchSyncState.RULES_COMMANDS_SEND, - SwitchSyncState.METERS_COMMANDS_SEND, - SwitchSyncState.GROUPS_COMMANDS_SEND, + SwitchSyncState.SEND_REMOVE_COMMANDS, + SwitchSyncState.SEND_MODIFY_COMMANDS, + SwitchSyncState.SEND_INSTALL_COMMANDS, SwitchSyncState.LOGICAL_PORTS_COMMANDS_SEND, SwitchSyncState.FINISHED, SwitchSyncState.FINISHED_WITH_ERROR diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchValidateService.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchValidateService.java index d6aff1c4670..cad3628e97a 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchValidateService.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/SwitchValidateService.java @@ -104,7 +104,7 @@ public void dispatchErrorMessage(ErrorData payload, MessageCookie cookie) throws SwitchValidateContext context = SwitchValidateContext.builder() .error(new SpeakerFailureException(payload)) .build(); - handle(cookie, SwitchValidateEvent.ERROR, context); + handle(cookie, SwitchValidateEvent.ERROR_RECEIVED, context); } @Override @@ -121,9 +121,10 @@ public void dispatchHeavyOperationMessage(InfoData payload, MessageCookie cookie * Handle switch validate request. */ public void handleSwitchValidateRequest(String key, SwitchValidateRequest request) { + SwitchManagerCarrierCookieDecorator fsmCarrier = new SwitchManagerCarrierCookieDecorator(carrier, key); SwitchValidateFsm fsm = builder.newStateMachine( - SwitchValidateState.START, carrier, key, request, validationService, persistenceManager); + SwitchValidateState.START, fsmCarrier, key, request, validationService, persistenceManager); MeterRegistryHolder.getRegistry().ifPresent(registry -> { Sample sample = LongTaskTimer.builder("fsm.active_execution") .register(registry) @@ -188,6 +189,7 @@ private void handle(MessageCookie cookie, SwitchValidateEvent event, SwitchValid if (handler == null) { throw new MessageDispatchException(cookie); } + context.setRequestCookie(cookie.getNested()); handle(handler, event, context); } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/CommandBuilderImpl.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/CommandBuilderImpl.java index 79da81dd8c5..02eff16d95d 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/CommandBuilderImpl.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/CommandBuilderImpl.java @@ -16,455 +16,33 @@ package org.openkilda.wfm.topology.switchmanager.service.impl; import static java.lang.String.format; -import static org.openkilda.model.cookie.Cookie.SERVER_42_FLOW_RTT_OUTPUT_VLAN_COOKIE; -import static org.openkilda.model.cookie.Cookie.SERVER_42_FLOW_RTT_OUTPUT_VXLAN_COOKIE; -import static org.openkilda.model.cookie.Cookie.SERVER_42_ISL_RTT_OUTPUT_COOKIE; -import org.openkilda.messaging.command.flow.BaseFlow; -import org.openkilda.messaging.command.flow.BaseInstallFlow; -import org.openkilda.messaging.command.flow.InstallServer42Flow; -import org.openkilda.messaging.command.flow.InstallServer42Flow.InstallServer42FlowBuilder; -import org.openkilda.messaging.command.flow.InstallSharedFlow; -import org.openkilda.messaging.command.flow.ModifyDefaultMeterForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.ModifyFlowMeterForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.ReinstallDefaultFlowForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.ReinstallServer42FlowForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.RemoveFlow; import org.openkilda.messaging.command.grpc.CreateOrUpdateLogicalPortRequest; import org.openkilda.messaging.command.grpc.DeleteLogicalPortRequest; -import org.openkilda.messaging.command.switches.DeleteRulesCriteria; -import org.openkilda.messaging.info.rule.FlowApplyActions; -import org.openkilda.messaging.info.rule.FlowEntry; -import org.openkilda.messaging.info.rule.FlowInstructions; -import org.openkilda.messaging.info.rule.FlowMatchField; import org.openkilda.messaging.info.switches.LogicalPortInfoEntry; -import org.openkilda.messaging.info.switches.MeterInfoEntry; -import org.openkilda.model.Flow; -import org.openkilda.model.FlowEncapsulationType; -import org.openkilda.model.FlowMirrorPoints; -import org.openkilda.model.FlowPath; -import org.openkilda.model.FlowTransitEncapsulation; -import org.openkilda.model.GroupId; import org.openkilda.model.IpSocketAddress; -import org.openkilda.model.MirrorConfig; -import org.openkilda.model.MirrorConfig.MirrorConfigData; -import org.openkilda.model.MirrorGroup; -import org.openkilda.model.PathId; -import org.openkilda.model.PathSegment; import org.openkilda.model.Switch; import org.openkilda.model.SwitchId; -import org.openkilda.model.SwitchProperties; -import org.openkilda.model.cookie.Cookie; -import org.openkilda.model.cookie.CookieBase.CookieType; -import org.openkilda.model.cookie.FlowSegmentCookie; -import org.openkilda.model.cookie.FlowSharedSegmentCookie; -import org.openkilda.model.cookie.FlowSharedSegmentCookie.SharedSegmentType; -import org.openkilda.model.cookie.PortColourCookie; import org.openkilda.persistence.PersistenceManager; -import org.openkilda.persistence.repositories.FlowPathRepository; -import org.openkilda.persistence.repositories.FlowRepository; -import org.openkilda.persistence.repositories.MirrorGroupRepository; -import org.openkilda.persistence.repositories.SwitchPropertiesRepository; import org.openkilda.persistence.repositories.SwitchRepository; -import org.openkilda.wfm.share.flow.resources.EncapsulationResources; -import org.openkilda.wfm.share.flow.resources.FlowResourcesConfig; -import org.openkilda.wfm.share.flow.resources.FlowResourcesManager; -import org.openkilda.wfm.share.flow.service.FlowCommandFactory; import org.openkilda.wfm.topology.switchmanager.error.SwitchNotFoundException; import org.openkilda.wfm.topology.switchmanager.mappers.LogicalPortMapper; -import org.openkilda.wfm.topology.switchmanager.model.GroupInstallContext; import org.openkilda.wfm.topology.switchmanager.service.CommandBuilder; -import com.fasterxml.uuid.Generators; -import com.fasterxml.uuid.NoArgGenerator; -import com.google.common.annotations.VisibleForTesting; import com.google.common.collect.Sets; -import lombok.NonNull; import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang.math.NumberUtils; import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.Optional; -import java.util.Set; import java.util.stream.Collectors; -import java.util.stream.Stream; @Slf4j public class CommandBuilderImpl implements CommandBuilder { - private final NoArgGenerator transactionIdGenerator = Generators.timeBasedGenerator(); - - private final FlowRepository flowRepository; - private final FlowPathRepository flowPathRepository; - private final SwitchPropertiesRepository switchPropertiesRepository; - private final MirrorGroupRepository mirrorGroupRepository; - private final FlowCommandFactory flowCommandFactory = new FlowCommandFactory(); - private final FlowResourcesManager flowResourcesManager; private final SwitchRepository switchRepository; - public CommandBuilderImpl(PersistenceManager persistenceManager, FlowResourcesConfig flowResourcesConfig) { - this.flowRepository = persistenceManager.getRepositoryFactory().createFlowRepository(); - this.flowPathRepository = persistenceManager.getRepositoryFactory().createFlowPathRepository(); - this.switchPropertiesRepository = persistenceManager.getRepositoryFactory().createSwitchPropertiesRepository(); - this.mirrorGroupRepository = persistenceManager.getRepositoryFactory().createMirrorGroupRepository(); + public CommandBuilderImpl(PersistenceManager persistenceManager) { this.switchRepository = persistenceManager.getRepositoryFactory().createSwitchRepository(); - this.flowResourcesManager = new FlowResourcesManager(persistenceManager, flowResourcesConfig); - } - - @Override - public List buildCommandsToSyncMissingRules(SwitchId switchId, List switchRules) { - - List commands = new ArrayList<>(buildInstallDefaultRuleCommands(switchId, switchRules)); - commands.addAll(buildInstallFlowSharedRuleCommands(switchId, switchRules)); - - flowPathRepository.findBySegmentDestSwitch(switchId) - .forEach(flowPath -> { - FlowSegmentCookie mirrorCookie = flowPath.getCookie().toBuilder().mirror(true).build(); - boolean switchRulesContainsFlowPathCookie = switchRules.contains(flowPath.getCookie().getValue()); - boolean switchRulesContainsMirrorCookie = switchRules.contains(mirrorCookie.getValue()); - if (switchRulesContainsFlowPathCookie || switchRulesContainsMirrorCookie) { - PathSegment segment = flowPath.getSegments().stream() - .filter(pathSegment -> pathSegment.getDestSwitchId().equals(switchId)) - .findAny() - .orElseThrow(() -> new IllegalStateException( - format("PathSegment not found, path %s, switch %s", flowPath, switchId))); - log.info("Rule {} is to be (re)installed on switch {}", flowPath.getCookie(), switchId); - commands.addAll(buildInstallCommandFromSegment(flowPath, segment, - switchRulesContainsFlowPathCookie, switchRulesContainsMirrorCookie)); - } - }); - - SwitchProperties switchProperties = getSwitchProperties(switchId); - flowPathRepository.findByEndpointSwitch(switchId) - .forEach(flowPath -> { - FlowSegmentCookie mirrorCookie = flowPath.getCookie().toBuilder().mirror(true).build(); - boolean switchRulesContainsFlowPathCookie = switchRules.contains(flowPath.getCookie().getValue()); - boolean switchRulesContainsMirrorCookie = switchRules.contains(mirrorCookie.getValue()); - if (switchRulesContainsFlowPathCookie || switchRulesContainsMirrorCookie) { - Flow flow = getFlow(flowPath); - if (flowPath.isOneSwitchFlow()) { - log.info("One-switch flow {} is to be (re)installed on switch {}", - flowPath.getCookie(), switchId); - SwitchId swId = flowPath.isForward() ? flow.getDestSwitchId() : flow.getSrcSwitchId(); - int port = flowPath.isForward() ? flow.getDestPort() : flow.getSrcPort(); - if (switchRulesContainsMirrorCookie) { - MirrorConfig mirrorConfig = makeMirrorConfig(flowPath, swId, port); - commands.add(flowCommandFactory.makeOneSwitchMirrorRule(flow, flowPath, mirrorConfig)); - } - if (switchRulesContainsFlowPathCookie) { - commands.add(flowCommandFactory.makeOneSwitchRule(flow, flowPath)); - } - } else if (flowPath.getSrcSwitchId().equals(switchId)) { - log.info("Ingress flow {} is to be (re)installed on switch {}", - flowPath.getCookie(), switchId); - if (flowPath.getSegments().isEmpty()) { - log.warn("Output port was not found for ingress flow rule"); - } else { - PathSegment foundIngressSegment = flowPath.getSegments().get(0); - EncapsulationResources encapsulationResources = getEncapsulationResources( - flowPath, flow); - if (switchRulesContainsMirrorCookie) { - MirrorConfig mirrorConfig = makeMirrorConfig(flowPath, - foundIngressSegment.getSrcSwitchId(), foundIngressSegment.getSrcPort()); - commands.add(flowCommandFactory.buildInstallIngressMirrorFlow(flow, flowPath, - foundIngressSegment.getSrcPort(), encapsulationResources, - foundIngressSegment.isSrcWithMultiTable(), mirrorConfig)); - } - if (switchRulesContainsFlowPathCookie) { - commands.add(flowCommandFactory.buildInstallIngressFlow(flow, flowPath, - foundIngressSegment.getSrcPort(), encapsulationResources, - foundIngressSegment.isSrcWithMultiTable())); - } - } - } - } - - long server42Cookie = flowPath.getCookie().toBuilder() - .type(CookieType.SERVER_42_FLOW_RTT_INGRESS) - .build() - .getValue(); - if (switchRules.contains(server42Cookie) && !flowPath.isOneSwitchFlow() - && flowPath.getSrcSwitchId().equals(switchId)) { - log.info("Ingress server 42 flow {} is to be (re)installed on switch {}", - server42Cookie, switchId); - - if (flowPath.getSegments().isEmpty()) { - log.warn("Output port was not found for server 42 ingress flow rule {}", server42Cookie); - } else { - Flow flow = getFlow(flowPath); - PathSegment foundIngressSegment = flowPath.getSegments().get(0); - EncapsulationResources encapsulationResources = getEncapsulationResources(flowPath, flow); - commands.add(flowCommandFactory.buildInstallServer42IngressFlow( - flow, flowPath, foundIngressSegment.getSrcPort(), - switchProperties.getServer42Port(), switchProperties.getServer42MacAddress(), - encapsulationResources, foundIngressSegment.isSrcWithMultiTable())); - } - } - - long loopCookie = flowPath.getCookie().toBuilder().looped(true).build().getValue(); - if (switchRules.contains(loopCookie)) { - log.info("Loop rule with cookie {} is to be reinstalled on switch {}", loopCookie, switchId); - Flow flow = getFlow(flowPath); - EncapsulationResources encapsulationResources = getEncapsulationResources( - flowPath, flow); - if (flowPath.getSrcSwitch().getSwitchId().equals(switchId)) { - boolean srcWithMultiTable = flowPath.getSegments().get(0).isSrcWithMultiTable(); - commands.add(flowCommandFactory.buildInstallIngressLoopFlow(flow, flowPath, - encapsulationResources, srcWithMultiTable)); - } else { - PathSegment lastSegment = flowPath.getSegments().get(flowPath.getSegments().size() - 1); - boolean destWithMultiTable = lastSegment.isDestWithMultiTable(); - commands.add(flowCommandFactory.buildInstallTransitLoopFlow(flow, flowPath, - lastSegment.getDestPort(), encapsulationResources, destWithMultiTable)); - } - - } - }); - - return commands; - } - - private Flow getFlow(FlowPath flowPath) { - return flowRepository.findById(flowPath.getFlow().getFlowId()) - .orElseThrow(() -> - new IllegalStateException(format("Abandon FlowPath found: %s", flowPath))); - } - - private EncapsulationResources getEncapsulationResources(FlowPath flowPath, Flow flow) { - return flowResourcesManager.getEncapsulationResources(flowPath.getPathId(), - flow.getOppositePathId(flowPath.getPathId()) - .orElseThrow(() -> new IllegalStateException( - format("Flow %s does not have reverse path for %s", - flow.getFlowId(), flowPath.getPathId()))), - flow.getEncapsulationType()) - .orElseThrow(() -> new IllegalStateException( - format("Encapsulation resources are not found for path %s", flowPath))); - } - - /** - * Some default rules require additional properties to be installed. This method filters such rules. - */ - private static boolean isDefaultRuleWithSpecialRequirements(long cookie) { - return cookie == SERVER_42_FLOW_RTT_OUTPUT_VLAN_COOKIE - || cookie == SERVER_42_FLOW_RTT_OUTPUT_VXLAN_COOKIE - || new PortColourCookie(cookie).getType() == CookieType.SERVER_42_FLOW_RTT_INPUT - || cookie == SERVER_42_ISL_RTT_OUTPUT_COOKIE - || new PortColourCookie(cookie).getType() == CookieType.SERVER_42_ISL_RTT_INPUT; - } - - /** - * Some default rules require additional properties to be installed. This method creates commands for such rules. - */ - private List buildInstallSpecialDefaultRuleCommands(SwitchId switchId, List switchRules) { - SwitchProperties properties = getSwitchProperties(switchId); - - List commands = new ArrayList<>(); - for (Long cookie : switchRules) { - InstallServer42FlowBuilder command = InstallServer42Flow.builder() - .transactionId(transactionIdGenerator.generate()) - .cookie(cookie) - .switchId(switchId) - .multiTable(properties.isMultiTable()) - .inputPort(0) - .outputPort(0) - .server42Vlan(properties.getServer42Vlan()) - .server42MacAddress(properties.getServer42MacAddress()); - - if (cookie == SERVER_42_FLOW_RTT_OUTPUT_VLAN_COOKIE) { - commands.add(command - .id("SWMANAGER_SERVER_42_FLOW_RTT_OUTPUT_VLAN_RULE_INSTALL") - .outputPort(properties.getServer42Port()) - .build()); - } else if (cookie == SERVER_42_FLOW_RTT_OUTPUT_VXLAN_COOKIE) { - commands.add(command - .id("SWMANAGER_SERVER_42_FLOW_RTT_OUTPUT_VXLAN_RULE_INSTALL") - .outputPort(properties.getServer42Port()) - .build()); - } else if (new PortColourCookie(cookie).getType() == CookieType.SERVER_42_FLOW_RTT_INPUT) { - commands.add(command - .id("SWMANAGER_SERVER_42_FLOW_RTT_INPUT_RULE_INSTALL") - .inputPort(properties.getServer42Port()) - .build()); - } else if (cookie == SERVER_42_ISL_RTT_OUTPUT_COOKIE) { - commands.add(command - .id("SWMANAGER_SERVER_42_ISL_RTT_OUTPUT_RULE_INSTALL") - .outputPort(properties.getServer42Port()) - .build()); - } else if (new PortColourCookie(cookie).getType() == CookieType.SERVER_42_ISL_RTT_INPUT) { - commands.add(command - .id("SWMANAGER_SERVER_42_ISL_RTT_INPUT_RULE_INSTALL") - .inputPort(properties.getServer42Port()) - .build()); - } else { - log.warn("Got request for installation of unknown rule {} on switch {}", cookie, switchId); - } - } - return commands; - } - - private SwitchProperties getSwitchProperties(SwitchId switchId) { - return switchPropertiesRepository.findBySwitchId(switchId).orElseThrow( - () -> new IllegalStateException(format("Switch properties not found for switch %s", switchId))); - } - - private List buildInstallDefaultRuleCommands(SwitchId switchId, List switchRules) { - - List commands = new ArrayList<>( - buildInstallSpecialDefaultRuleCommands( - switchId, switchRules.stream() - .filter(CommandBuilderImpl::isDefaultRuleWithSpecialRequirements) - .collect(Collectors.toList()))); - - switchRules.stream() - .filter(Cookie::isDefaultRule) - .filter(cookie -> !isDefaultRuleWithSpecialRequirements(cookie)) - .map(cookie -> new BaseInstallFlow(transactionIdGenerator.generate(), "SWMANAGER_DEFAULT_RULE_INSTALL", - cookie, switchId, 0, 0, false)) - .forEach(commands::add); - - return commands; - } - - private List buildInstallFlowSharedRuleCommands(SwitchId switchId, List switchRules) { - List results = new ArrayList<>(); - for (long rawCookie : switchRules) { - FlowSharedSegmentCookie cookie = new FlowSharedSegmentCookie(rawCookie); - if (cookie.getType() != CookieType.SHARED_OF_FLOW) { - continue; - } - - if (cookie.getSegmentType() == SharedSegmentType.QINQ_OUTER_VLAN) { - results.add(new InstallSharedFlow( - transactionIdGenerator.generate(), "SWMANAGER_SHARED_FLOW_INSTALL", rawCookie, switchId)); - } else if (cookie.getSegmentType() == SharedSegmentType.SERVER42_QINQ_OUTER_VLAN) { - results.add(InstallServer42Flow.builder() - .id("SWMANAGER_SERVER42_SHARED_FLOW_INSTALL") - .transactionId(transactionIdGenerator.generate()) - .cookie(cookie.getValue()) - .switchId(switchId) - .multiTable(true) - .inputPort(cookie.getPortNumber()) - .outputPort(0) - .build()); - } - } - - return results; - } - - @Override - public List buildCommandsToRemoveExcessRules(SwitchId switchId, - List flows, - List excessRulesCookies) { - return flows.stream() - .filter(flow -> excessRulesCookies.contains(flow.getCookie())) - .map(entry -> buildRemoveFlowWithoutMeterFromFlowEntry(switchId, entry)) - .collect(Collectors.toList()); - } - - @Override - public List buildCommandsToReinstallRules( - SwitchId switchId, List reinstallRulesCookies) { - - SwitchProperties properties = getSwitchProperties(switchId); - List commands = new ArrayList<>(); - - for (Long cookie : reinstallRulesCookies) { - if (isDefaultRuleWithSpecialRequirements(cookie)) { - commands.add(new ReinstallServer42FlowForSwitchManagerRequest( - switchId, cookie, properties.getServer42MacAddress(), properties.getServer42Vlan(), - properties.getServer42Port())); - } else { - commands.add(new ReinstallDefaultFlowForSwitchManagerRequest(switchId, cookie)); - } - } - - return commands; - } - - @Override - public List buildCommandsToModifyMisconfiguredMeters( - SwitchId switchId, List misconfiguredDefaultMeters, List misconfiguredFlowMeters) { - List commands = misconfiguredDefaultMeters.stream() - .map(meterId -> new ModifyDefaultMeterForSwitchManagerRequest(switchId, meterId)) - .collect(Collectors.toList()); - - for (MeterInfoEntry meter : misconfiguredFlowMeters) { - long rate = Optional.ofNullable(meter.getExpected().getRate()).orElse(meter.getRate()); - commands.add(new ModifyFlowMeterForSwitchManagerRequest(switchId, meter.getMeterId(), rate)); - } - return commands; - } - - @Override - public List buildGroupInstallContexts(SwitchId switchId, List groupIds) { - List groupInstallContexts = new ArrayList<>(); - - Map mirrorGroups = new HashMap<>(); - groupIds.stream() - .map(GroupId::new) - .map(mirrorGroupRepository::findByGroupId) - .flatMap(o -> o.map(Stream::of).orElseGet(Stream::empty)) - .forEach(mirrorGroup -> mirrorGroups.put(mirrorGroup.getPathId(), mirrorGroup)); - - flowPathRepository.findBySegmentDestSwitch(switchId) - .forEach(flowPath -> { - if (mirrorGroups.containsKey(flowPath.getPathId())) { - PathSegment segment = flowPath.getSegments().stream() - .filter(pathSegment -> pathSegment.getDestSwitchId().equals(switchId)) - .findAny() - .orElseThrow(() -> new IllegalStateException( - format("PathSegment not found, path %s, switch %s", flowPath, switchId))); - - Flow flow = flowPath.getFlow(); - if (segment.getDestSwitchId().equals(flowPath.getDestSwitchId())) { - MirrorConfig mirrorConfig - = makeMirrorConfig(flowPath, flow.getDestSwitchId(), flow.getDestPort()); - groupInstallContexts.add(GroupInstallContext.builder().mirrorConfig(mirrorConfig).build()); - - log.info("Group {} is to be (re)installed on switch {}", - mirrorConfig.getGroupId(), switchId); - } - } - }); - - flowPathRepository.findByEndpointSwitch(switchId) - .forEach(flowPath -> { - if (mirrorGroups.containsKey(flowPath.getPathId())) { - Flow flow = getFlow(flowPath); - if (flowPath.isOneSwitchFlow()) { - SwitchId swId = flowPath.isForward() ? flow.getDestSwitchId() : flow.getSrcSwitchId(); - int port = flowPath.isForward() ? flow.getDestPort() : flow.getSrcPort(); - MirrorConfig mirrorConfig = makeMirrorConfig(flowPath, swId, port); - groupInstallContexts.add(GroupInstallContext.builder().mirrorConfig(mirrorConfig).build()); - log.info("Group {} is to be (re)installed on switch {}", - mirrorConfig.getGroupId(), switchId); - } else if (flowPath.getSrcSwitchId().equals(switchId)) { - if (flowPath.getSegments().isEmpty()) { - log.warn("Output port was not found for mirror config"); - } else { - PathSegment foundIngressSegment = flowPath.getSegments().get(0); - MirrorConfig mirrorConfig = makeMirrorConfig(flowPath, - foundIngressSegment.getSrcSwitchId(), foundIngressSegment.getSrcPort()); - EncapsulationResources encapsulation = getEncapsulationResources(flowPath, flow); - groupInstallContexts.add(GroupInstallContext.builder() - .mirrorConfig(mirrorConfig) - .encapsulation( - new FlowTransitEncapsulation(encapsulation.getTransitEncapsulationId(), - encapsulation.getEncapsulationType())) - .egressSwitchId(flowPath.getDestSwitchId()) - .build()); - log.info("Group {} is to be (re)installed on switch {}", - mirrorConfig.getGroupId(), switchId); - } - } - } - }); - - return groupInstallContexts; } @Override @@ -496,120 +74,4 @@ private String getSwitchIpAddress(SwitchId switchId) { return Optional.ofNullable(sw.getSocketAddress()).map(IpSocketAddress::getAddress).orElseThrow( () -> new IllegalStateException(format("Unable to get IP address of switch %s", switchId))); } - - @VisibleForTesting - RemoveFlow buildRemoveFlowWithoutMeterFromFlowEntry(SwitchId switchId, FlowEntry entry) { - Optional entryMatch = Optional.ofNullable(entry.getMatch()); - - Integer inPort = entryMatch.map(FlowMatchField::getInPort).map(Integer::valueOf).orElse(null); - - FlowEncapsulationType encapsulationType = FlowEncapsulationType.TRANSIT_VLAN; - Integer encapsulationId = null; - Integer vlan = entryMatch.map(FlowMatchField::getVlanVid).map(Integer::valueOf).orElse(null); - if (vlan != null) { - encapsulationId = vlan; - } else { - Integer tunnelId = entryMatch.map(FlowMatchField::getTunnelId).map(Integer::decode).orElse(null); - - if (tunnelId != null) { - encapsulationId = tunnelId; - encapsulationType = FlowEncapsulationType.VXLAN; - } - } - - Optional actions = Optional.ofNullable(entry.getInstructions()) - .map(FlowInstructions::getApplyActions); - - Integer outPort = actions - .map(FlowApplyActions::getFlowOutput) - .filter(NumberUtils::isNumber) - .map(Integer::valueOf) - .orElse(null); - - SwitchId ingressSwitchId = entryMatch.map(FlowMatchField::getEthSrc).map(SwitchId::new).orElse(null); - Long metadataValue = entryMatch.map(FlowMatchField::getMetadataValue).map(Long::decode).orElse(null); - Long metadataMask = entryMatch.map(FlowMatchField::getMetadataMask).map(Long::decode).orElse(null); - - DeleteRulesCriteria criteria = new DeleteRulesCriteria(entry.getCookie(), inPort, encapsulationId, - 0, outPort, encapsulationType, ingressSwitchId, metadataValue, metadataMask); - - return RemoveFlow.builder() - .transactionId(transactionIdGenerator.generate()) - .flowId("SWMANAGER_BATCH_REMOVE") - .cookie(entry.getCookie()) - .switchId(switchId) - .criteria(criteria) - .build(); - } - - private List buildInstallCommandFromSegment(FlowPath flowPath, PathSegment segment, - boolean switchRulesContainsFlowPathCookie, - boolean switchRulesContainsMirrorCookie) { - if (segment.getSrcSwitchId().equals(segment.getDestSwitchId())) { - log.warn("One-switch flow segment {} is provided", flowPath.getCookie()); - return new ArrayList<>(); - } - - Optional foundFlow = flowRepository.findById(flowPath.getFlow().getFlowId()); - if (!foundFlow.isPresent()) { - log.warn("Flow with id {} was not found", flowPath.getFlow().getFlowId()); - return new ArrayList<>(); - } - Flow flow = foundFlow.get(); - - EncapsulationResources encapsulationResources = getEncapsulationResources(flowPath, flow); - - if (segment.getDestSwitchId().equals(flowPath.getDestSwitchId())) { - List commands = new ArrayList<>(); - if (switchRulesContainsMirrorCookie) { - MirrorConfig mirrorConfig = makeMirrorConfig(flowPath, flow.getDestSwitchId(), flow.getDestPort()); - commands.add(flowCommandFactory.buildInstallEgressMirrorFlow(flowPath, segment.getDestPort(), - encapsulationResources, segment.isDestWithMultiTable(), mirrorConfig)); - } - if (switchRulesContainsFlowPathCookie) { - commands.add(flowCommandFactory.buildInstallEgressFlow(flowPath, segment.getDestPort(), - encapsulationResources, segment.isDestWithMultiTable())); - } - return commands; - } else { - int segmentIdx = flowPath.getSegments().indexOf(segment); - if (segmentIdx < 0 || segmentIdx + 1 == flowPath.getSegments().size()) { - log.warn("Paired segment for switch {} and cookie {} has not been found", - segment.getDestSwitchId(), flowPath.getCookie()); - return new ArrayList<>(); - } - - PathSegment foundPairedFlowSegment = flowPath.getSegments().get(segmentIdx + 1); - - return Collections.singletonList(flowCommandFactory.buildInstallTransitFlow( - flowPath, segment.getDestSwitchId(), segment.getDestPort(), - foundPairedFlowSegment.getSrcPort(), encapsulationResources, - segment.isDestWithMultiTable())); - } - } - - private MirrorConfig makeMirrorConfig(@NonNull FlowPath flowPath, @NonNull SwitchId mirrorSwitchId, - int mirrorPort) { - MirrorConfig mirrorConfig = null; - FlowMirrorPoints flowMirrorPoints = flowPath.getFlowMirrorPointsSet().stream() - .filter(mirrorPoints -> mirrorSwitchId.equals(mirrorPoints.getMirrorSwitchId())) - .findFirst().orElse(null); - - if (flowMirrorPoints != null) { - Set mirrorConfigDataSet = flowMirrorPoints.getMirrorPaths().stream() - .map(mirrorPath -> - new MirrorConfigData(mirrorPath.getEgressPort(), mirrorPath.getEgressOuterVlan())) - .collect(Collectors.toSet()); - - if (!mirrorConfigDataSet.isEmpty()) { - mirrorConfig = MirrorConfig.builder() - .groupId(flowMirrorPoints.getMirrorGroupId()) - .flowPort(mirrorPort) - .mirrorConfigDataSet(mirrorConfigDataSet) - .build(); - } - } - - return mirrorConfig; - } } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/SpeakerWorkerService.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/SpeakerWorkerService.java index 52ffb281546..f22facfb9ea 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/SpeakerWorkerService.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/SpeakerWorkerService.java @@ -15,6 +15,8 @@ package org.openkilda.wfm.topology.switchmanager.service.impl; +import org.openkilda.floodlight.api.request.rulemanager.BaseSpeakerCommandsRequest; +import org.openkilda.floodlight.api.response.SpeakerResponse; import org.openkilda.messaging.Message; import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.command.CommandData; @@ -48,10 +50,22 @@ public SpeakerWorkerService(SpeakerCommandCarrier carrier) { */ public void sendFloodlightCommand(String key, CommandData command, MessageCookie cookie) throws PipelineException { log.debug("Got Floodlight request from hub bolt {}", command); - keyToRequest.put(key, new RequestContext(command, cookie)); + keyToRequest.put(key, new RequestContext(command, null, cookie)); carrier.sendFloodlightCommand(key, new CommandMessage(command, System.currentTimeMillis(), key)); } + /** + * Sends RuleManager request to Floodlight speaker. + * @param key unique operation's key. + * @param request request to be processed. + */ + public void sendFloodlightOfRequest(String key, BaseSpeakerCommandsRequest request, MessageCookie cookie) + throws PipelineException { + log.debug("Got Floodlight RuleManager request from hub bolt {}", request); + keyToRequest.put(key, new RequestContext(null, request, cookie)); + carrier.sendFloodlightOfRequest(key, request); + } + /** * Sends command to GRPC speaker. * @param key unique operation's key. @@ -59,7 +73,7 @@ public void sendFloodlightCommand(String key, CommandData command, MessageCookie */ public void sendGrpcCommand(String key, CommandData command, MessageCookie cookie) throws PipelineException { log.debug("Got GRPC request from hub bolt {}", command); - keyToRequest.put(key, new RequestContext(command, cookie)); + keyToRequest.put(key, new RequestContext(command, null, cookie)); carrier.sendGrpcCommand(key, new CommandMessage(command, key, cookie)); } @@ -80,6 +94,20 @@ public void handleResponse(String key, Message response) } } + /** + * Processes received OF speaker response and forwards it to the hub component. + */ + public void handleSpeakerResponse(String key, SpeakerResponse response) throws PipelineException { + log.debug("Got a response from speaker {}", response); + RequestContext pending = keyToRequest.remove(key); + if (pending != null) { + if (response.getMessageCookie() == null) { + response.setMessageCookie(pending.getCookie()); + } + carrier.sendSpeakerResponse(key, response); + } + } + /** * Handles operation timeout. * @param key operation identifier. @@ -98,6 +126,7 @@ public void handleTimeout(String key) throws PipelineException { @Value private static class RequestContext { CommandData payload; + BaseSpeakerCommandsRequest request; MessageCookie cookie; } } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/ValidationServiceImpl.java b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/ValidationServiceImpl.java index 61c894e0f8a..24acf76f9d2 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/ValidationServiceImpl.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/main/java/org/openkilda/wfm/topology/switchmanager/service/impl/ValidationServiceImpl.java @@ -161,7 +161,7 @@ public ValidateGroupsResult validateGroups(SwitchId switchId, List !presentGroupsIds.contains(entry.getGroupId())) .forEach(missingGroups::add); if (!missingGroups.isEmpty() && log.isErrorEnabled()) { - log.error("On switch {} the following groups are missed: {}", switchId, + log.warn("On switch {} the following groups are missed: {}", switchId, missingGroups.stream().map(x -> Integer.toString(x.getGroupId())) .collect(Collectors.joining(", ", "[", "]"))); } @@ -238,7 +238,8 @@ public ValidateLogicalPortsResult validateLogicalPorts(SwitchId switchId, List calculateMisconfiguredGroups(Set expected, Set actual) { @@ -382,7 +383,7 @@ public ValidateMetersResult validateMeters(SwitchId switchId, List misconfiguredMeters = newArrayList( - MeterInfoEntry.builder().cookie(COOKIE_1).meterId(METER_ID_1).build(), - MeterInfoEntry.builder().cookie(COOKIE_2).meterId(METER_ID_2).build(), - MeterInfoEntry.builder().cookie(COOKIE_3).meterId(METER_ID_3).build()); - - ValidateMetersResult validateMetersResult = new ValidateMetersResult( - newArrayList(), misconfiguredMeters, newArrayList(), newArrayList()); - - ValidationResult validationResult = new ValidationResult(new ArrayList<>(), true, - validateRulesResult, validateMetersResult, EMPTY_VALIDATE_GROUPS_RESULT, EMPTY_LOGICAL_PORTS_RESULT); - - SwitchSyncFsm fsm = new SwitchSyncFsm(null, null, null, - new SwitchValidateRequest(SWITCH_ID, true, true, true), validationResult); - - List modifyMeters = fsm.getModifyDefaultMeters(); - assertEquals(1, modifyMeters.size()); - assertEquals(METER_ID_3, modifyMeters.get(0).longValue()); - } - - @Test - public void getModifyDefaultMetersWithFlowMeters() { - ArrayList misconfiguredMeters = newArrayList( - MeterInfoEntry.builder().cookie(COOKIE_1).meterId((long) MeterId.MIN_FLOW_METER_ID).build()); - - ValidateMetersResult validateMetersResult = new ValidateMetersResult( - newArrayList(), misconfiguredMeters, newArrayList(), newArrayList()); - - ValidationResult validationResult = new ValidationResult(new ArrayList<>(), true, - EMPTY_VALIDATE_RULES_RESULT, validateMetersResult, EMPTY_VALIDATE_GROUPS_RESULT, - EMPTY_LOGICAL_PORTS_RESULT); - - SwitchSyncFsm fsm = new SwitchSyncFsm(null, null, null, - new SwitchValidateRequest(SWITCH_ID, true, true, true), validationResult); - - assertTrue(fsm.getModifyDefaultMeters().isEmpty()); - } -} diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/FlowEntryConverterTest.java b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/FlowEntryConverterTest.java new file mode 100644 index 00000000000..831c59a67c0 --- /dev/null +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/FlowEntryConverterTest.java @@ -0,0 +1,161 @@ +/* Copyright 2022 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.topology.switchmanager.mappers; + +import static org.junit.Assert.assertArrayEquals; +import static org.junit.Assert.assertEquals; +import static org.openkilda.wfm.topology.switchmanager.mappers.FlowEntryConverter.INSTANCE; + +import org.openkilda.messaging.info.rule.FlowApplyActions; +import org.openkilda.messaging.info.rule.FlowEntry; +import org.openkilda.messaging.info.rule.FlowInstructions; +import org.openkilda.messaging.info.rule.FlowMatchField; +import org.openkilda.messaging.info.rule.FlowSetFieldAction; +import org.openkilda.model.MeterId; +import org.openkilda.model.cookie.Cookie; +import org.openkilda.rulemanager.Field; +import org.openkilda.rulemanager.FlowSpeakerData; +import org.openkilda.rulemanager.Instructions; +import org.openkilda.rulemanager.OfFlowFlag; +import org.openkilda.rulemanager.OfMetadata; +import org.openkilda.rulemanager.OfTable; +import org.openkilda.rulemanager.OfVersion; +import org.openkilda.rulemanager.action.Action; +import org.openkilda.rulemanager.action.SetFieldAction; +import org.openkilda.rulemanager.match.FieldMatch; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; + +public class FlowEntryConverterTest { + + // Test constants + public static final int METER_ID_VALUE = 1; + public static final Long OF_METADATA_VALUE = 2L; + public static final Long OF_METADATA_MASK = 3L; + public static final int COOKIE_VALUE = 4; + public static final int SET_FIELD_VALUE = 5; + public static final int MATCH_VALUE = 6; + public static final long MATCH_MASK = 1L; + public static final int DURATION_SECONDS = 7; + public static final int DURATION_NANOSECONDS = 8; + public static final int PACKET_COUNT = 9; + public static final int PRIORITY = 10; + public static final int IDLE_TIMEOUT = 11; + public static final int HARD_TIMEOUT = 12; + public static final int BYTE_COUNT = 13; + + public static MeterId METER_ID = new MeterId(METER_ID_VALUE); + public static OfTable OF_TABLE_FIELD = OfTable.INGRESS; + public static OfVersion OF_VERSION = OfVersion.OF_15; + public static OfMetadata OF_METADATA = new OfMetadata(OF_METADATA_VALUE, OF_METADATA_MASK); + public static Cookie COOKIE = new Cookie(COOKIE_VALUE); + public static Field FIELD = Field.METADATA; + public static SetFieldAction SET_FIELD_ACTION = new SetFieldAction(SET_FIELD_VALUE, FIELD); + + public static List applyActions = new LinkedList<>(); + public static Set writeActions = new HashSet<>(); + public static Set flags = new HashSet<>(); + public static Set matches = new HashSet<>(); + + public static Instructions instructions; + public static FlowSpeakerData data; + + @BeforeClass + public static void initializeData() { + + applyActions.add(SET_FIELD_ACTION); + matches.add(new FieldMatch(OF_METADATA_VALUE, (long) OF_METADATA_MASK, Field.METADATA)); + + for (Field field : Field.values()) { + if (field.equals(Field.METADATA)) { + continue; + } + matches.add(new FieldMatch(MATCH_VALUE, MATCH_MASK, field)); + } + + flags.add(OfFlowFlag.RESET_COUNTERS); + instructions = new Instructions(applyActions, writeActions, METER_ID, OF_TABLE_FIELD, OF_METADATA); + + data = FlowSpeakerData.builder() + .cookie(COOKIE) + .durationSeconds(DURATION_SECONDS) + .durationNanoSeconds(DURATION_NANOSECONDS) + .table(OF_TABLE_FIELD) + .packetCount(PACKET_COUNT) + .ofVersion(OF_VERSION) + .priority(PRIORITY) + .idleTimeout(IDLE_TIMEOUT) + .hardTimeout(HARD_TIMEOUT) + .byteCount(BYTE_COUNT) + .match(matches) + .instructions(instructions) + .flags(flags) + .build(); + } + + @Test + public void mapFlowEntryTest() { + + FlowEntry entry = INSTANCE.toFlowEntry(data); + // General asserts + assertEquals(COOKIE_VALUE, entry.getCookie()); + assertEquals(DURATION_SECONDS, entry.getDurationSeconds()); + assertEquals(DURATION_NANOSECONDS, entry.getDurationNanoSeconds()); + assertEquals(OF_TABLE_FIELD.getTableId(), entry.getTableId()); + assertEquals(PACKET_COUNT, entry.getPacketCount()); + assertEquals(OF_VERSION.toString(), entry.getVersion()); + assertEquals(PRIORITY, entry.getPriority()); + assertEquals(IDLE_TIMEOUT, entry.getIdleTimeout()); + assertEquals(HARD_TIMEOUT, entry.getHardTimeout()); + assertEquals(BYTE_COUNT, entry.getByteCount()); + + // Field match + FlowMatchField entryMatchField = entry.getMatch(); + // Metadata field case + assertEquals(OF_METADATA_MASK.toString(), entryMatchField.getMetadataMask()); + assertEquals(OF_METADATA_VALUE.toString(), entryMatchField.getMetadataValue()); + // Other cases + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getEthType()); + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getEthSrc()); + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getEthDst()); + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getInPort()); + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getIpProto()); + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getUdpSrc()); + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getUdpDst()); + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getVlanVid()); + assertEquals(Integer.toString(MATCH_VALUE), entryMatchField.getTunnelId()); + + // Instructions + FlowInstructions instructions = entry.getInstructions(); + assertEquals(METER_ID.getValue(), instructions.getGoToMeter().longValue()); + assertEquals(OF_TABLE_FIELD.getTableId(), instructions.getGoToTable().longValue()); + FlowApplyActions applyActions = instructions.getApplyActions(); + FlowSetFieldAction setFieldAction = applyActions.getSetFieldActions().get(0); + assertEquals(SET_FIELD_ACTION.getField().toString(), setFieldAction.getFieldName()); + assertEquals(Long.toString(SET_FIELD_ACTION.getValue()), setFieldAction.getFieldValue()); + + // Flags + assertArrayEquals(flags.stream().map(OfFlowFlag::toString).toArray(), entry.getFlags()); + + } + +} diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/GroupEntryConverterTest.java b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/GroupEntryConverterTest.java new file mode 100644 index 00000000000..b0ac5951816 --- /dev/null +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/GroupEntryConverterTest.java @@ -0,0 +1,118 @@ +/* Copyright 2022 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.topology.switchmanager.mappers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.openkilda.wfm.topology.switchmanager.mappers.GroupEntryConverter.INSTANCE; + +import org.openkilda.messaging.info.switches.GroupInfoEntry; +import org.openkilda.model.GroupId; +import org.openkilda.model.IPv4Address; +import org.openkilda.model.MacAddress; +import org.openkilda.model.SwitchId; +import org.openkilda.rulemanager.Field; +import org.openkilda.rulemanager.GroupSpeakerData; +import org.openkilda.rulemanager.OfVersion; +import org.openkilda.rulemanager.ProtoConstants; +import org.openkilda.rulemanager.action.Action; +import org.openkilda.rulemanager.action.ActionType; +import org.openkilda.rulemanager.action.PortOutAction; +import org.openkilda.rulemanager.action.PushVxlanAction; +import org.openkilda.rulemanager.action.SetFieldAction; +import org.openkilda.rulemanager.group.Bucket; +import org.openkilda.rulemanager.group.GroupType; +import org.openkilda.rulemanager.group.WatchGroup; +import org.openkilda.rulemanager.group.WatchPort; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public class GroupEntryConverterTest { + public static final int GROUP_ID_VALUE = 1; + public static final long SWITCH_ID_VALUE = 2L; + public static final String MAC_ADDRESS_VALUE = "00:00:00:00:00:01"; + public static final String IPV4_ADDRESS_VALUE = "255.255.255.255"; + public static final int PORT_NUMBER_VALUE = 3; + public static final int SET_FIELD_VALUE = 4; + public static final int VNI_OVS_ACTION_VALUE = 7; + public static final int UDP_OVS_ACTION_VALUE = 8; + + public static GroupId GROUP_ID = new GroupId(GROUP_ID_VALUE); + public static GroupType GROUP_TYPE = GroupType.ALL; + public static UUID RANDOM_UUID = UUID.randomUUID(); + public static SwitchId SWITCH_ID = new SwitchId(SWITCH_ID_VALUE); + public static OfVersion OF_VERSION = OfVersion.OF_15; + public static MacAddress MAC_ADDRESS = new MacAddress(MAC_ADDRESS_VALUE); + public static IPv4Address IPV4_ADDRESS = new IPv4Address(IPV4_ADDRESS_VALUE); + public static ProtoConstants.PortNumber PORT_NUMBER = new ProtoConstants.PortNumber(PORT_NUMBER_VALUE); + + public static PortOutAction PORT_OUT_ACTION = new PortOutAction(PORT_NUMBER); + public static SetFieldAction SET_FIELD_ACTION = new SetFieldAction(SET_FIELD_VALUE, Field.VLAN_VID); + + public static PushVxlanAction PUSH_VXLAN_OVS_ACTION = PushVxlanAction.builder() + .type(ActionType.PUSH_VXLAN_OVS) + .vni(VNI_OVS_ACTION_VALUE) + .srcMacAddress(MAC_ADDRESS) + .dstMacAddress(MAC_ADDRESS) + .srcIpv4Address(IPV4_ADDRESS) + .dstIpv4Address(IPV4_ADDRESS) + .udpSrc(UDP_OVS_ACTION_VALUE) + .build(); + + public static GroupSpeakerData data; + public static Set writeActions = new HashSet<>(); + public static List buckets = new LinkedList<>(); + + @BeforeClass + public static void initializeData() { + writeActions.add(SET_FIELD_ACTION); + writeActions.add(PORT_OUT_ACTION); + writeActions.add(PUSH_VXLAN_OVS_ACTION); + + buckets.add(new Bucket(WatchGroup.ALL, WatchPort.ANY, writeActions)); + + data = GroupSpeakerData.builder() + .uuid(RANDOM_UUID) + .switchId(SWITCH_ID) + .dependsOn(Collections.emptyList()) + .ofVersion(OF_VERSION) + .groupId(GROUP_ID) + .type(GROUP_TYPE) + .buckets(buckets) + .build(); + } + + @Test + public void mapGroupEntryTest() { + + GroupInfoEntry entry = INSTANCE.toGroupEntry(data); + + assertEquals(GROUP_ID.intValue(), entry.getGroupId()); + GroupInfoEntry.BucketEntry testBucket = entry.getGroupBuckets().get(0); + assertEquals(PORT_NUMBER.getPortNumber(), testBucket.getPort()); + assertEquals(SET_FIELD_ACTION.getValue(), (long) testBucket.getVlan()); + assertEquals(PUSH_VXLAN_OVS_ACTION.getVni(), (int) testBucket.getVni()); + } + +} + diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/MeterEntryConverterTest.java b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/MeterEntryConverterTest.java new file mode 100644 index 00000000000..932d7474696 --- /dev/null +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/MeterEntryConverterTest.java @@ -0,0 +1,63 @@ +/* Copyright 2022 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.topology.switchmanager.mappers; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.openkilda.wfm.topology.switchmanager.mappers.MeterEntryConverter.INSTANCE; + +import org.openkilda.messaging.info.switches.MeterInfoEntry; +import org.openkilda.model.MeterId; +import org.openkilda.rulemanager.MeterFlag; +import org.openkilda.rulemanager.MeterSpeakerData; + +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.HashSet; +import java.util.Set; + +public class MeterEntryConverterTest { + public static MeterId METER_ID = new MeterId(3L); + private static final Long RATE = 4L; + private static final Long BURST_SIZE = 5L; + + public static MeterSpeakerData data; + + public static Set flags = new HashSet<>(); + + @BeforeClass + public static void initializeData() { + flags.add(MeterFlag.BURST); + + data = MeterSpeakerData.builder() + .meterId(METER_ID) + .rate(RATE) + .burst(BURST_SIZE) + .flags(flags) + .build(); + + } + + @Test + public void mapMeterEntryTest() { + MeterInfoEntry entry = INSTANCE.toMeterEntry(data); + + assertEquals(METER_ID.getValue(), entry.getMeterId()); + assertEquals(RATE, entry.getRate()); + assertEquals(BURST_SIZE, entry.getBurstSize()); + assertEquals(flags.toArray()[0].toString(), entry.getFlags()[0]); + } +} diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/ValidationMapperTest.java b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/ValidationMapperTest.java new file mode 100644 index 00000000000..bf7b8785b32 --- /dev/null +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/mappers/ValidationMapperTest.java @@ -0,0 +1,300 @@ +/* Copyright 2022 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.topology.switchmanager.mappers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.openkilda.messaging.info.switches.GroupInfoEntry; +import org.openkilda.messaging.info.switches.GroupsValidationEntry; +import org.openkilda.messaging.info.switches.LogicalPortInfoEntry; +import org.openkilda.messaging.info.switches.LogicalPortsValidationEntry; +import org.openkilda.messaging.info.switches.MeterInfoEntry; +import org.openkilda.messaging.info.switches.MetersValidationEntry; +import org.openkilda.messaging.info.switches.RulesValidationEntry; +import org.openkilda.messaging.info.switches.SwitchValidationResponse; +import org.openkilda.model.GroupId; +import org.openkilda.model.IPv4Address; +import org.openkilda.model.LagLogicalPort; +import org.openkilda.model.MacAddress; +import org.openkilda.model.MeterId; +import org.openkilda.model.SwitchId; +import org.openkilda.model.cookie.Cookie; +import org.openkilda.rulemanager.Field; +import org.openkilda.rulemanager.FlowSpeakerData; +import org.openkilda.rulemanager.GroupSpeakerData; +import org.openkilda.rulemanager.Instructions; +import org.openkilda.rulemanager.MeterFlag; +import org.openkilda.rulemanager.MeterSpeakerData; +import org.openkilda.rulemanager.OfFlowFlag; +import org.openkilda.rulemanager.OfMetadata; +import org.openkilda.rulemanager.OfTable; +import org.openkilda.rulemanager.OfVersion; +import org.openkilda.rulemanager.ProtoConstants; +import org.openkilda.rulemanager.action.Action; +import org.openkilda.rulemanager.action.ActionType; +import org.openkilda.rulemanager.action.PortOutAction; +import org.openkilda.rulemanager.action.PushVxlanAction; +import org.openkilda.rulemanager.action.SetFieldAction; +import org.openkilda.rulemanager.group.Bucket; +import org.openkilda.rulemanager.group.GroupType; +import org.openkilda.rulemanager.group.WatchGroup; +import org.openkilda.rulemanager.group.WatchPort; +import org.openkilda.rulemanager.match.FieldMatch; +import org.openkilda.wfm.topology.switchmanager.model.SwitchValidationContext; +import org.openkilda.wfm.topology.switchmanager.model.ValidateGroupsResult; +import org.openkilda.wfm.topology.switchmanager.model.ValidateLogicalPortsResult; +import org.openkilda.wfm.topology.switchmanager.model.ValidateMetersResult; +import org.openkilda.wfm.topology.switchmanager.model.ValidateRulesResult; + +import com.google.common.collect.Lists; +import org.junit.BeforeClass; +import org.junit.Test; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +public class ValidationMapperTest { + + public static final int GROUP_ID_VALUE = 1; + public static final String MAC_ADDRESS_VALUE = "00:00:00:00:00:01"; + public static final String IPV4_ADDRESS_VALUE = "255.255.255.255"; + public static final int PORT_NUMBER_VALUE = 3; + public static final int SET_FIELD_VALUE = 4; + public static final int VNI_NOVIFLOW_ACTION_VALUE = 5; + public static final int UDP_NOVIFLOW_ACTION_VALUE = 6; + public static final int VNI_OVS_ACTION_VALUE = 7; + public static final int UDP_OVS_ACTION_VALUE = 8; + public static final int METER_ID_VALUE = 9; + public static final int OF_METADATA_VALUE = 10; + public static final int OF_METADATA_MASK = 11; + public static final int COOKIE_VALUE = 12; + public static final int MATCH_VALUE = 13; + public static final long MATCH_MASK = 3L; + public static final int DURATION_SECONDS = 14; + public static final int DURATION_NANOSECONDS = 15; + public static final int PACKET_COUNT = 16; + public static final int IDLE_TIMEOUT = 18; + public static final int HARD_TIMEOUT = 19; + public static final int BYTE_COUNT = 20; + public static Long RATE = 4L; + public static Long BURST_SIZE = 5L; + public static int LAG_PORT = 21; + public static int PHYSICAL_PORT_1 = 22; + public static int PHYSICAL_PORT_2 = 23; + + public static MeterId METER_ID = new MeterId(METER_ID_VALUE); + public static OfTable OF_TABLE_FIELD = OfTable.INGRESS; + public static OfVersion OF_VERSION = OfVersion.OF_15; + public static OfMetadata OF_METADATA = new OfMetadata(OF_METADATA_VALUE, OF_METADATA_MASK); + public static Cookie COOKIE = new Cookie(COOKIE_VALUE); + public static Field FIELD = Field.METADATA; + public static SetFieldAction SET_FIELD_ACTION = new SetFieldAction(SET_FIELD_VALUE, FIELD); + public static GroupId GROUP_ID = new GroupId(GROUP_ID_VALUE); + public static GroupType GROUP_TYPE = GroupType.ALL; + public static UUID RANDOM_UUID = UUID.randomUUID(); + public static MacAddress MAC_ADDRESS = new MacAddress(MAC_ADDRESS_VALUE); + public static IPv4Address IPV4_ADDRESS = new IPv4Address(IPV4_ADDRESS_VALUE); + public static ProtoConstants.PortNumber PORT_NUMBER = new ProtoConstants.PortNumber(PORT_NUMBER_VALUE); + public static PortOutAction PORT_OUT_ACTION = new PortOutAction(PORT_NUMBER); + public static PushVxlanAction PUSH_VXLAN_NOVIFLOW_ACTION = PushVxlanAction.builder() + .type(ActionType.PUSH_VXLAN_NOVIFLOW) + .vni(VNI_NOVIFLOW_ACTION_VALUE) + .srcMacAddress(MAC_ADDRESS) + .dstMacAddress(MAC_ADDRESS) + .srcIpv4Address(IPV4_ADDRESS) + .dstIpv4Address(IPV4_ADDRESS) + .udpSrc(UDP_NOVIFLOW_ACTION_VALUE) + .build(); + public static PushVxlanAction PUSH_VXLAN_OVS_ACTION = PushVxlanAction.builder() + .type(ActionType.PUSH_VXLAN_OVS) + .vni(VNI_OVS_ACTION_VALUE) + .srcMacAddress(MAC_ADDRESS) + .dstMacAddress(MAC_ADDRESS) + .srcIpv4Address(IPV4_ADDRESS) + .dstIpv4Address(IPV4_ADDRESS) + .udpSrc(UDP_OVS_ACTION_VALUE) + .build(); + + public static Set groupSpeakerWriteActions = new HashSet<>(); + public static List buckets = new LinkedList<>(); + + private static GroupSpeakerData initializeGroupSpeakerData(SwitchId uniqueSwitchIdField) { + groupSpeakerWriteActions.add(SET_FIELD_ACTION); + groupSpeakerWriteActions.add(PORT_OUT_ACTION); + groupSpeakerWriteActions.add(PUSH_VXLAN_NOVIFLOW_ACTION); + groupSpeakerWriteActions.add(PUSH_VXLAN_OVS_ACTION); + + buckets.add(new Bucket(WatchGroup.ALL, WatchPort.ANY, groupSpeakerWriteActions)); + + return GroupSpeakerData.builder() + .uuid(RANDOM_UUID) + .switchId(uniqueSwitchIdField) + .dependsOn(Collections.emptyList()) + .ofVersion(OF_VERSION) + .groupId(GROUP_ID) + .type(GROUP_TYPE) + .buckets(buckets) + .build(); + } + + public static List applyActions = new LinkedList<>(); + public static Set flowSpeakerWriteActions = new HashSet<>(); + public static Set flowFlags = new HashSet<>(); + public static Set matches = new HashSet<>(); + public static Instructions instructions; + + private static FlowSpeakerData initializeFlowSpeakerData(int uniquePriorityField) { + + applyActions.add(SET_FIELD_ACTION); + for (Field field : Field.values()) { + matches.add(new FieldMatch(MATCH_VALUE, MATCH_MASK, field)); + } + flowFlags.add(OfFlowFlag.RESET_COUNTERS); + + instructions = new Instructions(applyActions, flowSpeakerWriteActions, METER_ID, OF_TABLE_FIELD, OF_METADATA); + + return FlowSpeakerData.builder() + .cookie(COOKIE) + .durationSeconds(DURATION_SECONDS) + .durationNanoSeconds(DURATION_NANOSECONDS) + .table(OF_TABLE_FIELD) + .packetCount(PACKET_COUNT) + .ofVersion(OF_VERSION) + .priority(uniquePriorityField) + .idleTimeout(IDLE_TIMEOUT) + .hardTimeout(HARD_TIMEOUT) + .byteCount(BYTE_COUNT) + .match(matches) + .instructions(instructions) + .flags(flowFlags) + .build(); + } + + public static Set meterFlags = new HashSet<>(); + + private static MeterSpeakerData initializeMeterSpeakerData(MeterId uniqueMeterIdValue) { + meterFlags.add(MeterFlag.BURST); + + return MeterSpeakerData.builder() + .meterId(uniqueMeterIdValue) + .rate(RATE) + .burst(BURST_SIZE) + .flags(meterFlags) + .build(); + + } + + private static LagLogicalPort initializeLogicalPortData(SwitchId uniqueSwitchIdField) { + return new LagLogicalPort(uniqueSwitchIdField, LAG_PORT, + Lists.newArrayList(PHYSICAL_PORT_1, PHYSICAL_PORT_2)); + } + + private static List missingGroups = new LinkedList<>(); + private static List properGroups = new LinkedList<>(); + private static List excessGroups = new LinkedList<>(); + private static List misconfiguredGroups = new LinkedList<>(); + private static Set missingRules = new HashSet<>(); + private static Set properRules = new HashSet<>(); + private static Set excessRules = new HashSet<>(); + private static Set misconfiguredRules = new HashSet<>(); + private static List missingMeters = new LinkedList<>(); + private static List misconfiguredMeters = new LinkedList<>(); + private static List properMeters = new LinkedList<>(); + private static List excessMeters = new LinkedList<>(); + private static List properPorts = new LinkedList<>(); + private static List missingPorts = new LinkedList<>(); + private static List excessPorts = new LinkedList<>(); + private static List misconfiguredPorts = new LinkedList<>(); + + private static ValidateGroupsResult groupsResult; + private static ValidateRulesResult rulesResult; + private static ValidateMetersResult metersResult; + private static ValidateLogicalPortsResult logicalPortsResult; + + @BeforeClass + public static void initializeData() { + missingGroups.add(GroupEntryConverter.INSTANCE.toGroupEntry(initializeGroupSpeakerData(new SwitchId(1)))); + properGroups.add(GroupEntryConverter.INSTANCE.toGroupEntry(initializeGroupSpeakerData(new SwitchId(2)))); + excessGroups.add(GroupEntryConverter.INSTANCE.toGroupEntry(initializeGroupSpeakerData(new SwitchId(3)))); + misconfiguredGroups.add(GroupEntryConverter.INSTANCE.toGroupEntry(initializeGroupSpeakerData(new SwitchId(4)))); + + missingRules.add(1L); + properRules.add(2L); + excessRules.add(3L); + misconfiguredRules.add(4L); + + missingMeters.add(MeterEntryConverter.INSTANCE.toMeterEntry(initializeMeterSpeakerData(new MeterId(1)))); + misconfiguredMeters.add(MeterEntryConverter.INSTANCE.toMeterEntry(initializeMeterSpeakerData( + new MeterId(2)))); + properMeters.add(MeterEntryConverter.INSTANCE.toMeterEntry(initializeMeterSpeakerData( + new MeterId(3)))); + excessMeters.add(MeterEntryConverter.INSTANCE.toMeterEntry(initializeMeterSpeakerData( + new MeterId(4)))); + + properPorts.add(LogicalPortMapper.INSTANCE.map(initializeLogicalPortData(new SwitchId(1)))); + missingPorts.add(LogicalPortMapper.INSTANCE.map(initializeLogicalPortData(new SwitchId(2)))); + excessPorts.add(LogicalPortMapper.INSTANCE.map(initializeLogicalPortData(new SwitchId(3)))); + misconfiguredPorts.add(LogicalPortMapper.INSTANCE.map(initializeLogicalPortData(new SwitchId(4)))); + + groupsResult = new ValidateGroupsResult(missingGroups, properGroups, excessGroups, misconfiguredGroups); + rulesResult = new ValidateRulesResult(missingRules, properRules, excessRules, misconfiguredRules); + metersResult = new ValidateMetersResult(missingMeters, misconfiguredMeters, properMeters, excessMeters); + logicalPortsResult = new ValidateLogicalPortsResult(properPorts, + missingPorts, excessPorts, misconfiguredPorts, "Test error"); + } + + @Test + public void mapValidationTest() { + + SwitchValidationContext context = SwitchValidationContext.builder(new SwitchId(1)) + .validateGroupsResult(groupsResult) + .metersValidationReport(metersResult) + .validateLogicalPortResult(logicalPortsResult) + .ofFlowsValidationReport(rulesResult) + .build(); + + SwitchValidationResponse response = ValidationMapper.INSTANCE.toSwitchResponse(context); + + GroupsValidationEntry groupsEntry = response.getGroups(); + assertEquals(missingGroups, groupsEntry.getMissing()); + assertEquals(properGroups, groupsEntry.getProper()); + assertEquals(misconfiguredGroups, groupsEntry.getMisconfigured()); + assertEquals(excessGroups, groupsEntry.getExcess()); + + RulesValidationEntry rulesEntry = response.getRules(); + assertEquals(new ArrayList<>(properRules), rulesEntry.getProper()); + assertEquals(new ArrayList<>(excessRules), rulesEntry.getExcess()); + assertEquals(new ArrayList<>(misconfiguredRules), rulesEntry.getMisconfigured()); + assertEquals(new ArrayList<>(missingRules), rulesEntry.getMissing()); + + LogicalPortsValidationEntry portsEntry = response.getLogicalPorts(); + assertEquals(properPorts, portsEntry.getProper()); + assertEquals(excessPorts, portsEntry.getExcess()); + assertEquals(misconfiguredPorts, portsEntry.getMisconfigured()); + assertEquals(missingPorts, portsEntry.getMissing()); + + MetersValidationEntry metersEntry = response.getMeters(); + assertEquals(properMeters, metersEntry.getProper()); + assertEquals(excessMeters, metersEntry.getExcess()); + assertEquals(misconfiguredMeters, metersEntry.getMisconfigured()); + assertEquals(missingMeters, metersEntry.getMissing()); + } +} diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/SwitchSyncServiceTest.java b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/SwitchSyncServiceTest.java index 7a77ecaf224..764d9da1b0d 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/SwitchSyncServiceTest.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/SwitchSyncServiceTest.java @@ -17,41 +17,44 @@ import static com.google.common.collect.Sets.newHashSet; import static java.util.Collections.emptyList; +import static java.util.Collections.emptyMap; import static java.util.Collections.singletonList; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoInteractions; import static org.mockito.Mockito.verifyNoMoreInteractions; -import static org.mockito.Mockito.when; +import org.openkilda.floodlight.api.request.rulemanager.FlowCommand; +import org.openkilda.floodlight.api.request.rulemanager.MeterCommand; +import org.openkilda.floodlight.api.request.rulemanager.OfCommand; +import org.openkilda.floodlight.api.response.rulemanager.SpeakerCommandResponse; +import org.openkilda.messaging.MessageContext; import org.openkilda.messaging.MessageCookie; -import org.openkilda.messaging.command.CommandData; -import org.openkilda.messaging.command.flow.InstallFlowForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.InstallIngressFlow; -import org.openkilda.messaging.command.flow.RemoveFlow; -import org.openkilda.messaging.command.flow.RemoveFlowForSwitchManagerRequest; -import org.openkilda.messaging.command.switches.DeleterMeterForSwitchManagerRequest; import org.openkilda.messaging.command.switches.SwitchValidateRequest; import org.openkilda.messaging.error.ErrorData; import org.openkilda.messaging.error.ErrorMessage; import org.openkilda.messaging.error.ErrorType; import org.openkilda.messaging.info.InfoMessage; -import org.openkilda.messaging.info.flow.FlowInstallResponse; -import org.openkilda.messaging.info.flow.FlowRemoveResponse; +import org.openkilda.messaging.info.grpc.DeleteLogicalPortResponse; import org.openkilda.messaging.info.rule.FlowEntry; -import org.openkilda.messaging.info.switches.DeleteMeterResponse; import org.openkilda.messaging.info.switches.MeterInfoEntry; import org.openkilda.messaging.info.switches.SwitchSyncResponse; -import org.openkilda.model.FlowEncapsulationType; import org.openkilda.model.FlowPathDirection; -import org.openkilda.model.OutputVlanType; +import org.openkilda.model.MeterId; import org.openkilda.model.SwitchId; +import org.openkilda.model.cookie.Cookie; import org.openkilda.model.cookie.FlowSegmentCookie; +import org.openkilda.rulemanager.FlowSpeakerData; +import org.openkilda.rulemanager.GroupSpeakerData; +import org.openkilda.rulemanager.MeterSpeakerData; +import org.openkilda.rulemanager.SpeakerData; import org.openkilda.wfm.error.MessageDispatchException; import org.openkilda.wfm.error.UnexpectedInputException; +import org.openkilda.wfm.topology.switchmanager.bolt.SwitchManagerHub.OfCommandAction; import org.openkilda.wfm.topology.switchmanager.model.ValidateGroupsResult; import org.openkilda.wfm.topology.switchmanager.model.ValidateLogicalPortsResult; import org.openkilda.wfm.topology.switchmanager.model.ValidateMetersResult; @@ -62,6 +65,7 @@ import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; +import org.mockito.Captor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; @@ -72,8 +76,6 @@ public class SwitchSyncServiceTest { private static SwitchId SWITCH_ID = new SwitchId(0x0000000000000001L); - private static SwitchId INGRESS_SWITCH_ID = new SwitchId(0x0000000000000002L); - private static SwitchId EGRESS_SWITCH_ID = new SwitchId(0x0000000000000002L); private static String FLOW_ID = "flow_id"; private static String KEY = "KEY"; private static long EXCESS_COOKIE = new FlowSegmentCookie(FlowPathDirection.FORWARD, 1).getValue(); @@ -84,6 +86,9 @@ public class SwitchSyncServiceTest { @Mock private CommandBuilder commandBuilder; + @Captor + private ArgumentCaptor> captor; + private SwitchSyncService service; private SwitchValidateRequest request; @@ -94,6 +99,11 @@ public class SwitchSyncServiceTest { private List misconfiguredRules; private List excessMeters; + private List expectedEntries; + private List actualFlows; + private List actualMeters; + private List actualGroups; + @Before public void setUp() { service = new SwitchSyncService(carrier, commandBuilder); @@ -103,17 +113,15 @@ public void setUp() { new FlowSegmentCookie(FlowPathDirection.FORWARD, 7).getValue(), 0, 0, 0, 0, "", 0, 0, 0, 0, null, null, null); - InstallIngressFlow installingRule = new InstallIngressFlow(UUID.randomUUID(), FLOW_ID, flowEntry.getCookie(), - SWITCH_ID, 1, 2, 50, 0, - 60, FlowEncapsulationType.TRANSIT_VLAN, OutputVlanType.POP, 10L, - 100L, EGRESS_SWITCH_ID, false, false, false, null); - when(commandBuilder.buildCommandsToSyncMissingRules(eq(SWITCH_ID), any())) - .thenReturn(singletonList(installingRule)); - missingRules = singletonList(flowEntry.getCookie()); excessRules = emptyList(); misconfiguredRules = emptyList(); excessMeters = emptyList(); + + expectedEntries = singletonList(FlowSpeakerData.builder().cookie(new Cookie(flowEntry.getCookie())).build()); + actualFlows = emptyList(); + actualMeters = emptyList(); + actualGroups = emptyList(); } @Test @@ -129,42 +137,27 @@ public void handleNothingRulesToSync() { verifyNoMoreInteractions(carrier); } - @Test - public void handleCommandBuilderMissingRulesException() { - String errorMessage = "test error"; - when(commandBuilder.buildCommandsToSyncMissingRules(eq(SWITCH_ID), any())) - .thenThrow(new IllegalArgumentException(errorMessage)); - - service.handleSwitchSync(KEY, request, makeValidationResult()); - - verify(commandBuilder).buildCommandsToSyncMissingRules(eq(SWITCH_ID), eq(missingRules)); - ArgumentCaptor errorCaptor = ArgumentCaptor.forClass(ErrorMessage.class); - verify(carrier).cancelTimeoutCallback(eq(KEY)); - verify(carrier).response(eq(KEY), errorCaptor.capture()); - assertEquals(errorMessage, errorCaptor.getValue().getData().getErrorMessage()); - - verifyNoMoreInteractions(commandBuilder); - verifyNoMoreInteractions(carrier); - } - @Test(expected = MessageDispatchException.class) public void reportErrorInCaseOfMissingHandler() throws UnexpectedInputException, MessageDispatchException { - service.dispatchWorkerMessage(new FlowInstallResponse(), new MessageCookie("dummy")); + service.dispatchWorkerMessage(new DeleteLogicalPortResponse("", 2, true), new MessageCookie("dummy")); } @Test public void handleRuleSyncSuccess() throws UnexpectedInputException, MessageDispatchException { service.handleSwitchSync(KEY, request, makeValidationResult()); - verify(commandBuilder).buildCommandsToSyncMissingRules(eq(SWITCH_ID), eq(missingRules)); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(CommandData.class)); + verify(carrier).sendOfCommandsToSpeaker(eq(KEY), captor.capture(), eq(OfCommandAction.INSTALL), eq(SWITCH_ID)); + assertEquals(1, captor.getValue().size()); + assertTrue(captor.getValue().get(0) instanceof FlowCommand); + FlowCommand flowCommand = (FlowCommand) captor.getValue().get(0); + assertEquals(flowEntry.getCookie(), flowCommand.getData().getCookie().getValue()); - service.dispatchWorkerMessage(new FlowInstallResponse(), new MessageCookie(KEY)); + service.dispatchWorkerMessage(buildSpeakerCommandResponse(), new MessageCookie(KEY)); verify(carrier).cancelTimeoutCallback(eq(KEY)); verify(carrier).response(eq(KEY), any(InfoMessage.class)); - verifyNoMoreInteractions(commandBuilder); + verifyNoInteractions(commandBuilder); verifyNoMoreInteractions(carrier); } @@ -172,8 +165,7 @@ public void handleRuleSyncSuccess() throws UnexpectedInputException, MessageDisp public void receiveRuleSyncTimeout() throws MessageDispatchException { service.handleSwitchSync(KEY, request, makeValidationResult()); - verify(commandBuilder).buildCommandsToSyncMissingRules(eq(SWITCH_ID), eq(missingRules)); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(CommandData.class)); + verify(carrier).sendOfCommandsToSpeaker(eq(KEY), any(List.class), eq(OfCommandAction.INSTALL), eq(SWITCH_ID)); service.timeout(new MessageCookie(KEY)); @@ -187,8 +179,7 @@ public void receiveRuleSyncTimeout() throws MessageDispatchException { public void receiveRuleSyncError() throws MessageDispatchException { service.handleSwitchSync(KEY, request, makeValidationResult()); - verify(commandBuilder).buildCommandsToSyncMissingRules(eq(SWITCH_ID), eq(missingRules)); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(InstallFlowForSwitchManagerRequest.class)); + verify(carrier).sendOfCommandsToSpeaker(eq(KEY), any(List.class), eq(OfCommandAction.INSTALL), eq(SWITCH_ID)); ErrorMessage errorMessage = getErrorMessage(); service.dispatchErrorMessage(errorMessage.getData(), new MessageCookie(KEY)); @@ -206,9 +197,12 @@ public void receiveMetersSyncError() throws MessageDispatchException { missingRules = emptyList(); excessMeters = singletonList( new MeterInfoEntry(EXCESS_COOKIE, EXCESS_COOKIE, FLOW_ID, 0L, 0L, new String[]{}, null, null)); + actualMeters = singletonList(MeterSpeakerData.builder() + .meterId(new MeterId(EXCESS_COOKIE)) + .build()); service.handleSwitchSync(KEY, request, makeValidationResult()); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(CommandData.class)); + verify(carrier).sendOfCommandsToSpeaker(eq(KEY), any(List.class), eq(OfCommandAction.DELETE), eq(SWITCH_ID)); service.dispatchErrorMessage(getErrorMessage().getData(), new MessageCookie(KEY)); @@ -238,39 +232,39 @@ public void handleSyncExcess() throws UnexpectedInputException, MessageDispatchE request = SwitchValidateRequest.builder().switchId(SWITCH_ID).performSync(true).removeExcess(true).build(); excessRules = singletonList(EXCESS_COOKIE); + actualFlows = singletonList(FlowSpeakerData.builder() + .cookie(new Cookie(EXCESS_COOKIE)) + .build()); + excessMeters = singletonList( new MeterInfoEntry(EXCESS_COOKIE, EXCESS_COOKIE, FLOW_ID, 0L, 0L, new String[]{}, null, null)); - - RemoveFlow removeFlow = RemoveFlow.builder() - .transactionId(UUID.randomUUID()) - .flowId(FLOW_ID) - .cookie(EXCESS_COOKIE) - .switchId(SWITCH_ID) - .meterId(EXCESS_COOKIE) - .build(); - when(commandBuilder.buildCommandsToRemoveExcessRules(eq(SWITCH_ID), any(), any())) - .thenReturn(singletonList(removeFlow)); + actualMeters = singletonList(MeterSpeakerData.builder() + .meterId(new MeterId(EXCESS_COOKIE)) + .build()); service.handleSwitchSync(KEY, request, makeValidationResult()); - verify(commandBuilder).buildCommandsToSyncMissingRules(eq(SWITCH_ID), eq(missingRules)); - verify(commandBuilder).buildCommandsToRemoveExcessRules( - eq(SWITCH_ID), eq(singletonList(flowEntry)), eq(excessRules)); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(DeleterMeterForSwitchManagerRequest.class)); - - service.dispatchWorkerMessage(new DeleteMeterResponse(true), new MessageCookie(KEY)); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(InstallFlowForSwitchManagerRequest.class)); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(RemoveFlowForSwitchManagerRequest.class)); - - service.dispatchWorkerMessage(new FlowInstallResponse(), new MessageCookie(KEY)); - service.dispatchWorkerMessage(new FlowRemoveResponse(), new MessageCookie(KEY)); - - verify(carrier, times(3)).sendCommandToSpeaker(eq(KEY), any(CommandData.class)); + verify(carrier).sendOfCommandsToSpeaker(eq(KEY), captor.capture(), eq(OfCommandAction.DELETE), eq(SWITCH_ID)); + assertEquals(2, captor.getValue().size()); + FlowCommand flowCommand = captor.getValue().stream() + .filter(command -> command instanceof FlowCommand) + .map(command -> (FlowCommand) command) + .findFirst().orElseThrow(() -> new IllegalStateException("Flow command not found")); + assertEquals(EXCESS_COOKIE, flowCommand.getData().getCookie().getValue()); + MeterCommand meterCommand = captor.getValue().stream() + .filter(command -> command instanceof MeterCommand) + .map(command -> (MeterCommand) command) + .findFirst().orElseThrow(() -> new IllegalStateException("Meter command not found")); + assertEquals(EXCESS_COOKIE, meterCommand.getData().getMeterId().getValue()); + service.dispatchWorkerMessage(buildSpeakerCommandResponse(), new MessageCookie(KEY)); + + verify(carrier).sendOfCommandsToSpeaker(eq(KEY), any(List.class), eq(OfCommandAction.INSTALL), eq(SWITCH_ID)); + service.dispatchWorkerMessage(buildSpeakerCommandResponse(), new MessageCookie(KEY)); verify(carrier).cancelTimeoutCallback(eq(KEY)); verify(carrier).response(eq(KEY), any(InfoMessage.class)); - verifyNoMoreInteractions(commandBuilder); + verifyNoInteractions(commandBuilder); verifyNoMoreInteractions(carrier); } @@ -280,55 +274,63 @@ public void handleSyncOnlyExcessMeters() throws UnexpectedInputException, Messag missingRules = emptyList(); excessMeters = singletonList( new MeterInfoEntry(EXCESS_COOKIE, EXCESS_COOKIE, FLOW_ID, 0L, 0L, new String[]{}, null, null)); + actualMeters = singletonList(MeterSpeakerData.builder() + .meterId(new MeterId(EXCESS_COOKIE)) + .build()); service.handleSwitchSync(KEY, request, makeValidationResult()); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(CommandData.class)); - service.dispatchWorkerMessage(new DeleteMeterResponse(true), new MessageCookie(KEY)); + verify(carrier).sendOfCommandsToSpeaker(eq(KEY), captor.capture(), eq(OfCommandAction.DELETE), eq(SWITCH_ID)); + service.dispatchWorkerMessage(buildSpeakerCommandResponse(), new MessageCookie(KEY)); verify(carrier).cancelTimeoutCallback(eq(KEY)); verify(carrier).response(eq(KEY), any(InfoMessage.class)); - verifyNoMoreInteractions(commandBuilder); + verifyNoInteractions(commandBuilder); verifyNoMoreInteractions(carrier); } @Test - public void handleSyncWhenNotProcessMeters() throws UnexpectedInputException, MessageDispatchException { + public void handleSyncWhenNotProcessMeters() { request = SwitchValidateRequest.builder().switchId(SWITCH_ID).performSync(true).removeExcess(true).build(); ValidationResult tempResult = makeValidationResult(); service.handleSwitchSync(KEY, request, new ValidationResult( - tempResult.getFlowEntries(), false, tempResult.getValidateRulesResult(), null, + tempResult.getFlowEntries(), false, + emptyList(), emptyList(), emptyList(), emptyList(), + tempResult.getValidateRulesResult(), null, new ValidateGroupsResult(emptyList(), emptyList(), emptyList(), emptyList()), - new ValidateLogicalPortsResult(emptyList(), emptyList(), emptyList(), emptyList()))); - - verify(commandBuilder).buildCommandsToSyncMissingRules(eq(SWITCH_ID), eq(missingRules)); - verify(carrier).sendCommandToSpeaker(eq(KEY), any(CommandData.class)); - - service.dispatchWorkerMessage(new FlowInstallResponse(), new MessageCookie(KEY)); + ValidateLogicalPortsResult.newEmpty())); verify(carrier).cancelTimeoutCallback(eq(KEY)); ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(InfoMessage.class); verify(carrier).response(eq(KEY), responseCaptor.capture()); assertNull(((SwitchSyncResponse) responseCaptor.getValue().getData()).getMeters()); - verifyNoMoreInteractions(commandBuilder); + verifyNoInteractions(commandBuilder); verifyNoMoreInteractions(carrier); } private ValidationResult makeValidationResult() { return new ValidationResult(singletonList(flowEntry), true, + expectedEntries, + actualFlows, + actualMeters, + actualGroups, new ValidateRulesResult(newHashSet(missingRules), newHashSet(flowEntry.getCookie()), newHashSet(excessRules), newHashSet(misconfiguredRules)), new ValidateMetersResult(emptyList(), emptyList(), emptyList(), excessMeters), new ValidateGroupsResult(emptyList(), emptyList(), emptyList(), emptyList()), - new ValidateLogicalPortsResult(emptyList(), emptyList(), emptyList(), emptyList())); + ValidateLogicalPortsResult.newEmpty()); } private ErrorMessage getErrorMessage() { return new ErrorMessage(new ErrorData(ErrorType.INTERNAL_ERROR, "message", "description"), System.currentTimeMillis(), KEY); } + + private SpeakerCommandResponse buildSpeakerCommandResponse() { + return new SpeakerCommandResponse(new MessageContext(), UUID.randomUUID(), SWITCH_ID, true, emptyMap()); + } } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/SwitchValidateServiceTest.java b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/SwitchValidateServiceTest.java index c45411d2576..d6dda846d76 100644 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/SwitchValidateServiceTest.java +++ b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/SwitchValidateServiceTest.java @@ -22,6 +22,7 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; @@ -31,6 +32,7 @@ import org.openkilda.messaging.MessageCookie; import org.openkilda.messaging.command.CommandData; +import org.openkilda.messaging.command.grpc.DumpLogicalPortsRequest; import org.openkilda.messaging.command.switches.SwitchValidateRequest; import org.openkilda.messaging.error.ErrorData; import org.openkilda.messaging.error.ErrorMessage; @@ -41,6 +43,7 @@ import org.openkilda.messaging.info.meter.MeterDumpResponse; import org.openkilda.messaging.info.meter.SwitchMeterUnsupported; import org.openkilda.messaging.info.switches.SwitchValidationResponse; +import org.openkilda.model.IpSocketAddress; import org.openkilda.model.MeterId; import org.openkilda.model.Switch; import org.openkilda.model.SwitchId; @@ -78,7 +81,13 @@ public class SwitchValidateServiceTest { private static final SwitchId SWITCH_ID = new SwitchId(0x0000000000000001L); private static final SwitchId SWITCH_ID_MISSING = new SwitchId(0x0000000000000002L); - private static final Switch SWITCH_1 = Switch.builder().switchId(SWITCH_ID).features(Sets.newHashSet(LAG)).build(); + private static final SwitchId LAG_SWITCH_ID = new SwitchId(0x0000000000000003L); + private static final Switch SWITCH_1 = Switch.builder().switchId(SWITCH_ID).build(); + private static final Switch SWITCH_2 = Switch.builder() + .switchId(LAG_SWITCH_ID) + .features(Sets.newHashSet(LAG)) + .socketAddress(new IpSocketAddress("127.127.127.127", 9999)) + .build(); private static final String KEY = "KEY"; @Mock @@ -101,6 +110,7 @@ public void setUp() { RepositoryFactory repositoryFactory = Mockito.mock(RepositoryFactory.class); SwitchRepository switchRepository = Mockito.mock(SwitchRepository.class); when(switchRepository.findById(eq(SWITCH_ID))).thenReturn(Optional.of(SWITCH_1)); + when(switchRepository.findById(eq(LAG_SWITCH_ID))).thenReturn(Optional.of(SWITCH_2)); when(switchRepository.findById(eq(SWITCH_ID_MISSING))).thenReturn(Optional.empty()); when(repositoryFactory.createSwitchRepository()).thenReturn(switchRepository); when(persistenceManager.getRepositoryFactory()).thenReturn(repositoryFactory); @@ -154,10 +164,15 @@ public void receiveTaskTimeout() throws UnexpectedInputException, MessageDispatc service.dispatchWorkerMessage( new FlowDumpResponse(SWITCH_ID, singletonList(flowSpeakerData)), new MessageCookie(KEY)); - service.timeout(new MessageCookie(KEY)); + ArgumentCaptor argument = ArgumentCaptor.forClass(MessageCookie.class); + verify(carrier, times(3)) + .sendCommandToSpeaker(any(CommandData.class), argument.capture()); + MessageCookie cookie = argument.getValue(); + service.timeout(cookie); verify(carrier).cancelTimeoutCallback(eq(KEY)); verify(carrier).errorResponse(eq(KEY), eq(ErrorType.OPERATION_TIMED_OUT), any(String.class), any(String.class)); + verifyNoMoreInteractions(carrier); verifyNoMoreInteractions(validationService); } @@ -168,8 +183,11 @@ public void receiveTaskError() throws UnexpectedInputException, MessageDispatchE service.dispatchWorkerMessage( new FlowDumpResponse(SWITCH_ID, singletonList(flowSpeakerData)), new MessageCookie(KEY)); + ArgumentCaptor argument = ArgumentCaptor.forClass(MessageCookie.class); + verify(carrier, times(3)) + .sendCommandToSpeaker(any(CommandData.class), argument.capture()); ErrorMessage errorMessage = getErrorMessage(); - service.dispatchErrorMessage(errorMessage.getData(), new MessageCookie(KEY)); + service.dispatchErrorMessage(errorMessage.getData(), argument.getValue()); verify(carrier).cancelTimeoutCallback(eq(KEY)); verify(carrier).errorResponse(eq(KEY), eq(errorMessage.getData().getErrorType()), any(String.class), @@ -199,8 +217,10 @@ public void validationWithoutMetersSuccess() throws UnexpectedInputException, Me request = SwitchValidateRequest.builder().switchId(SWITCH_ID).build(); service.handleSwitchValidateRequest(KEY, request); - verify(carrier, times(2)).sendCommandToSpeaker(eq(KEY), any(CommandData.class)); - verify(carrier, times(1)).runHeavyOperation(eq(KEY), eq(SWITCH_ID)); + verify(carrier, times(2)) + .sendCommandToSpeaker(any(CommandData.class), any(MessageCookie.class)); + verify(carrier, times(1)) + .runHeavyOperation(eq(SWITCH_ID), any(MessageCookie.class)); service.dispatchWorkerMessage( new FlowDumpResponse(SWITCH_ID, singletonList(flowSpeakerData)), new MessageCookie(KEY)); @@ -243,6 +263,46 @@ public void validationSuccessWithUnsupportedMeters() throws UnexpectedInputExcep verifyNoMoreInteractions(validationService); } + @Test + public void validationSuccessWithUnavailableGrpc() throws UnexpectedInputException, MessageDispatchException { + request = SwitchValidateRequest.builder().switchId(LAG_SWITCH_ID).processMeters(true).build(); + service.handleSwitchValidateRequest(KEY, request); + + verify(carrier, times(4)) + .sendCommandToSpeaker(any(CommandData.class), argThat(cookie -> KEY.equals(cookie.getValue()))); + verify(carrier, times(1)) + .runHeavyOperation(eq(LAG_SWITCH_ID), argThat(cookie -> KEY.equals(cookie.getValue()))); + + ArgumentCaptor cookieCaptor = ArgumentCaptor.forClass(MessageCookie.class); + verify(carrier, times(1)) + .sendCommandToSpeaker(any(DumpLogicalPortsRequest.class), cookieCaptor.capture()); + verifyNoMoreInteractions(carrier); + + service.dispatchWorkerMessage( + new FlowDumpResponse(LAG_SWITCH_ID, singletonList(flowSpeakerData)), new MessageCookie(KEY)); + service.dispatchWorkerMessage(new GroupDumpResponse(LAG_SWITCH_ID, emptyList()), new MessageCookie(KEY)); + service.dispatchWorkerMessage(new SwitchEntities(new ArrayList<>()), new MessageCookie(KEY)); + service.dispatchWorkerMessage(new MeterDumpResponse(LAG_SWITCH_ID, emptyList()), new MessageCookie(KEY)); + service.dispatchErrorMessage(getErrorMessage().getData(), cookieCaptor.getValue()); + + verify(validationService).validateRules(eq(LAG_SWITCH_ID), any(), any()); + verify(validationService).validateGroups(eq(LAG_SWITCH_ID), any(), any()); + verify(validationService).validateMeters(eq(LAG_SWITCH_ID), any(), any()); + + verify(carrier).cancelTimeoutCallback(eq(KEY)); + + ArgumentCaptor responseCaptor = ArgumentCaptor.forClass(InfoMessage.class); + verify(carrier).response(eq(KEY), responseCaptor.capture()); + SwitchValidationResponse response = (SwitchValidationResponse) responseCaptor.getValue().getData(); + + assertEquals(singletonList(flowSpeakerData.getCookie().getValue()), response.getRules().getMissing()); + assertEquals(response.getLogicalPorts().getError(), getErrorMessage().getData().getErrorMessage()); + + verifyNoMoreInteractions(carrier); + verifyNoMoreInteractions(validationService); + + } + @Test public void exceptionWhileValidation() throws UnexpectedInputException, MessageDispatchException { handleRequestAndInitDataReceive(); @@ -297,8 +357,10 @@ public void errorResponseOnSwitchNotFound() { private void handleRequestAndInitDataReceive() { service.handleSwitchValidateRequest(KEY, request); - verify(carrier, times(3)).sendCommandToSpeaker(eq(KEY), any(CommandData.class)); - verify(carrier, times(1)).runHeavyOperation(eq(KEY), eq(SWITCH_ID)); + verify(carrier, times(3)) + .sendCommandToSpeaker(any(CommandData.class), argThat(cookie -> KEY.equals(cookie.getValue()))); + verify(carrier, times(1)) + .runHeavyOperation(eq(SWITCH_ID), argThat(cookie -> KEY.equals(cookie.getValue()))); verifyNoMoreInteractions(carrier); } diff --git a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/impl/CommandBuilderImplTest.java b/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/impl/CommandBuilderImplTest.java deleted file mode 100644 index 5cf4d4b11c0..00000000000 --- a/src-java/swmanager-topology/swmanager-storm-topology/src/test/java/org/openkilda/wfm/topology/switchmanager/service/impl/CommandBuilderImplTest.java +++ /dev/null @@ -1,353 +0,0 @@ -/* Copyright 2018 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.topology.switchmanager.service.impl; - -import static java.util.Arrays.asList; -import static java.util.Collections.singleton; -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNull; -import static org.junit.Assert.assertTrue; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.when; -import static org.openkilda.model.cookie.Cookie.ROUND_TRIP_LATENCY_RULE_COOKIE; - -import org.openkilda.config.provider.PropertiesBasedConfigurationProvider; -import org.openkilda.messaging.command.flow.BaseFlow; -import org.openkilda.messaging.command.flow.InstallEgressFlow; -import org.openkilda.messaging.command.flow.InstallIngressFlow; -import org.openkilda.messaging.command.flow.InstallOneSwitchFlow; -import org.openkilda.messaging.command.flow.InstallTransitFlow; -import org.openkilda.messaging.command.flow.ReinstallDefaultFlowForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.ReinstallServer42FlowForSwitchManagerRequest; -import org.openkilda.messaging.command.flow.RemoveFlow; -import org.openkilda.messaging.command.switches.DeleteRulesCriteria; -import org.openkilda.messaging.info.rule.FlowApplyActions; -import org.openkilda.messaging.info.rule.FlowEntry; -import org.openkilda.messaging.info.rule.FlowInstructions; -import org.openkilda.messaging.info.rule.FlowMatchField; -import org.openkilda.model.Flow; -import org.openkilda.model.FlowEncapsulationType; -import org.openkilda.model.FlowPath; -import org.openkilda.model.FlowPathDirection; -import org.openkilda.model.MacAddress; -import org.openkilda.model.PathId; -import org.openkilda.model.PathSegment; -import org.openkilda.model.Switch; -import org.openkilda.model.SwitchId; -import org.openkilda.model.SwitchProperties; -import org.openkilda.model.TransitVlan; -import org.openkilda.model.cookie.Cookie; -import org.openkilda.model.cookie.FlowSegmentCookie; -import org.openkilda.persistence.PersistenceManager; -import org.openkilda.persistence.repositories.FlowPathRepository; -import org.openkilda.persistence.repositories.FlowRepository; -import org.openkilda.persistence.repositories.RepositoryFactory; -import org.openkilda.persistence.repositories.SwitchPropertiesRepository; -import org.openkilda.persistence.repositories.TransitVlanRepository; -import org.openkilda.wfm.share.flow.resources.FlowResourcesConfig; - -import com.google.common.collect.Lists; -import org.junit.BeforeClass; -import org.junit.Test; - -import java.util.Arrays; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.Optional; -import java.util.Properties; -import java.util.UUID; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -public class CommandBuilderImplTest { - - private static final SwitchId SWITCH_ID_A = new SwitchId("00:10"); - private static final SwitchId SWITCH_ID_B = new SwitchId("00:20"); - private static final SwitchId SWITCH_ID_C = new SwitchId("00:30"); - private static final MacAddress SERVER42_MAC_ADDRESS = new MacAddress("42:42:42:42:42:42"); - private static final int SERVER42_PORT = 1; - private static final int SERVER42_VLAN = 2; - - private static CommandBuilderImpl commandBuilder; - - @BeforeClass - public static void setUpOnce() { - Properties configProps = new Properties(); - configProps.setProperty("flow.meter-id.max", "40"); - configProps.setProperty("flow.meter-id.pool.chunks.count", "1"); - configProps.setProperty("flow.vlan.max", "50"); - - PropertiesBasedConfigurationProvider configurationProvider = - new PropertiesBasedConfigurationProvider(configProps); - FlowResourcesConfig flowResourcesConfig = configurationProvider.getConfiguration(FlowResourcesConfig.class); - commandBuilder = new CommandBuilderImpl(persistenceManager().build(), flowResourcesConfig); - } - - @Test - public void testCommandBuilder() { - List response = commandBuilder - .buildCommandsToSyncMissingRules(SWITCH_ID_B, - Stream.of(1L, 2L, 3L, 4L) - .map(effectiveId -> new FlowSegmentCookie(FlowPathDirection.FORWARD, effectiveId)) - .map(Cookie::getValue) - .collect(Collectors.toList())); - assertEquals(4, response.size()); - assertTrue(response.get(0) instanceof InstallEgressFlow); - assertTrue(response.get(1) instanceof InstallTransitFlow); - assertTrue(response.get(2) instanceof InstallOneSwitchFlow); - assertTrue(response.get(3) instanceof InstallIngressFlow); - } - - private static PersistenceManagerBuilder persistenceManager() { - return new PersistenceManagerBuilder(); - } - - private static class PersistenceManagerBuilder { - private final Map switchStorage = new HashMap<>(); - - private FlowRepository flowRepository = mock(FlowRepository.class); - private FlowPathRepository flowPathRepository = mock(FlowPathRepository.class); - private TransitVlanRepository transitVlanRepository = mock(TransitVlanRepository.class); - private SwitchPropertiesRepository switchPropertiesRepository = mock(SwitchPropertiesRepository.class); - - private FlowPath buildFlowAndPath(String flowId, SwitchId srcSwitchId, SwitchId destSwitchId, - int cookie, int transitVlan) { - boolean forward = srcSwitchId.compareTo(destSwitchId) <= 0; - Switch srcSwitch = Switch.builder().switchId(srcSwitchId).build(); - Switch destSwitch = Switch.builder().switchId(destSwitchId).build(); - - Flow flow = Flow.builder() - .flowId(flowId) - .srcSwitch(forward ? srcSwitch : destSwitch) - .destSwitch(forward ? destSwitch : srcSwitch) - .encapsulationType(FlowEncapsulationType.TRANSIT_VLAN) - .build(); - - FlowPath forwardPath = FlowPath.builder() - .pathId(new PathId(String.format( - "(%s-%s)--%s", srcSwitchId.toOtsdFormat(), destSwitchId.toOtsdFormat(), UUID.randomUUID()))) - .srcSwitch(srcSwitch) - .destSwitch(destSwitch) - .cookie(new FlowSegmentCookie(FlowPathDirection.FORWARD, cookie)) - .build(); - FlowPath reversePath = FlowPath.builder() - .pathId(new PathId(String.format( - "(%s-%s)--%s", destSwitchId.toOtsdFormat(), srcSwitchId.toOtsdFormat(), UUID.randomUUID()))) - .srcSwitch(destSwitch) - .destSwitch(srcSwitch) - .cookie(new FlowSegmentCookie(FlowPathDirection.REVERSE, cookie)) - .build(); - flow.setForwardPath(forward ? forwardPath : reversePath); - flow.setReversePath(forward ? reversePath : forwardPath); - - when(flowPathRepository.findById(eq(forwardPath.getPathId()))) - .thenReturn(Optional.of(forwardPath)); - when(flowPathRepository.findById(eq(reversePath.getPathId()))) - .thenReturn(Optional.of(reversePath)); - when(flowPathRepository.findByFlowId(eq(flowId))) - .thenReturn(asList(forwardPath, reversePath)); - when(flowRepository.findById(eq(flowId))) - .thenReturn(Optional.of(flow)); - - TransitVlan transitVlanEntity = TransitVlan.builder() - .flowId(flow.getFlowId()) - .pathId(forwardPath.getPathId()) - .vlan(transitVlan) - .build(); - when(transitVlanRepository.findByPathId(eq(forwardPath.getPathId()), any())) - .thenReturn(singleton(transitVlanEntity)); - when(switchPropertiesRepository.findBySwitchId(any())) - .thenReturn(Optional.ofNullable(SwitchProperties.builder() - .server42FlowRtt(true) - .server42MacAddress(SERVER42_MAC_ADDRESS) - .server42Port(SERVER42_PORT) - .server42Vlan(SERVER42_VLAN) - .build())); - - return forwardPath; - } - - private PathSegment buildSegment(PathId pathId, SwitchId srcSwitchId, SwitchId destSwitchId) { - return PathSegment.builder() - .pathId(pathId) - .srcSwitch(Switch.builder().switchId(srcSwitchId).build()) - .destSwitch(Switch.builder().switchId(destSwitchId).build()) - .build(); - } - - private PersistenceManager build() { - FlowPath flowPathA = buildFlowAndPath("A", SWITCH_ID_A, SWITCH_ID_B, 1, 1); - flowPathA.setSegments(asList(buildSegment(flowPathA.getPathId(), SWITCH_ID_A, SWITCH_ID_C), - buildSegment(flowPathA.getPathId(), SWITCH_ID_C, SWITCH_ID_B))); - - FlowPath flowPathB = buildFlowAndPath("B", SWITCH_ID_A, SWITCH_ID_C, 2, 1); - flowPathB.setSegments(asList(buildSegment(flowPathB.getPathId(), SWITCH_ID_A, SWITCH_ID_B), - buildSegment(flowPathB.getPathId(), SWITCH_ID_B, SWITCH_ID_C))); - - FlowPath flowPathC = buildFlowAndPath("C", SWITCH_ID_A, SWITCH_ID_A, 3, 1); - - FlowPath flowPathD = buildFlowAndPath("D", SWITCH_ID_B, SWITCH_ID_A, 4, 1); - flowPathD.setSegments(asList(buildSegment(flowPathD.getPathId(), SWITCH_ID_B, SWITCH_ID_A))); - - when(flowPathRepository.findBySegmentDestSwitch(eq(SWITCH_ID_B))) - .thenReturn(Arrays.asList(flowPathA, flowPathB)); - when(flowPathRepository.findByEndpointSwitch(eq(SWITCH_ID_B))) - .thenReturn(Arrays.asList(flowPathC, flowPathD)); - - RepositoryFactory repositoryFactory = mock(RepositoryFactory.class); - when(repositoryFactory.createFlowRepository()).thenReturn(flowRepository); - when(repositoryFactory.createFlowPathRepository()).thenReturn(flowPathRepository); - when(repositoryFactory.createTransitVlanRepository()).thenReturn(transitVlanRepository); - when(repositoryFactory.createSwitchPropertiesRepository()).thenReturn(switchPropertiesRepository); - - PersistenceManager persistenceManager = mock(PersistenceManager.class); - when(persistenceManager.getRepositoryFactory()).thenReturn(repositoryFactory); - return persistenceManager; - } - } - - @Test - public void shouldBuildRemoveFlowWithoutMeterFromFlowEntryWithTransitVlanEncapsulation() { - Long cookie = new FlowSegmentCookie(FlowPathDirection.FORWARD, 1).getValue(); - String inPort = "1"; - String inVlan = "10"; - String outPort = "2"; - FlowEntry flowEntry = buildFlowEntry(cookie, inPort, inVlan, outPort, null, false, null, null); - - RemoveFlow removeFlow = commandBuilder.buildRemoveFlowWithoutMeterFromFlowEntry(SWITCH_ID_A, flowEntry); - assertEquals(cookie, removeFlow.getCookie()); - - DeleteRulesCriteria criteria = removeFlow.getCriteria(); - assertEquals(cookie, criteria.getCookie()); - assertEquals(Integer.valueOf(inPort), criteria.getInPort()); - assertEquals(Integer.valueOf(inVlan), criteria.getEncapsulationId()); - assertEquals(Integer.valueOf(outPort), criteria.getOutPort()); - assertNull(criteria.getMetadataValue()); - assertNull(criteria.getMetadataMask()); - } - - @Test - public void shouldBuildRemoveFlowWithoutMeterFromFlowEntryWithStringOutPort() { - Long cookie = new FlowSegmentCookie(FlowPathDirection.FORWARD, 1).getValue(); - String inPort = "1"; - String inVlan = "10"; - String outPort = "in_port"; - FlowEntry flowEntry = buildFlowEntry(cookie, inPort, inVlan, outPort, null, false, null, null); - - RemoveFlow removeFlow = commandBuilder.buildRemoveFlowWithoutMeterFromFlowEntry(SWITCH_ID_A, flowEntry); - assertEquals(cookie, removeFlow.getCookie()); - - DeleteRulesCriteria criteria = removeFlow.getCriteria(); - assertEquals(cookie, criteria.getCookie()); - assertEquals(Integer.valueOf(inPort), criteria.getInPort()); - assertEquals(Integer.valueOf(inVlan), criteria.getEncapsulationId()); - assertNull(criteria.getOutPort()); - assertNull(criteria.getMetadataValue()); - assertNull(criteria.getMetadataMask()); - } - - @Test - public void shouldBuildRemoveFlowWithoutMeterFromFlowEntryWithVxlanEncapsulationIngress() { - Long cookie = new FlowSegmentCookie(FlowPathDirection.FORWARD, 1).getValue(); - String inPort = "1"; - String outPort = "2"; - String tunnelId = "10"; - String metadataValue = "0x15"; - String metadataMask = "0xFF"; - FlowEntry flowEntry = buildFlowEntry(cookie, inPort, null, outPort, tunnelId, true, metadataValue, - metadataMask); - - RemoveFlow removeFlow = commandBuilder.buildRemoveFlowWithoutMeterFromFlowEntry(SWITCH_ID_A, flowEntry); - assertEquals(cookie, removeFlow.getCookie()); - - DeleteRulesCriteria criteria = removeFlow.getCriteria(); - assertEquals(cookie, criteria.getCookie()); - assertEquals(Integer.valueOf(inPort), criteria.getInPort()); - assertNull(criteria.getEncapsulationId()); - assertEquals(Integer.valueOf(outPort), criteria.getOutPort()); - assertEquals(Long.decode(metadataValue), criteria.getMetadataValue()); - assertEquals(Long.decode(metadataMask), criteria.getMetadataMask()); - } - - @Test - public void shouldBuildRemoveFlowWithoutMeterFromFlowEntryWithVxlanEncapsulationTransitAndEgress() { - Long cookie = new FlowSegmentCookie(FlowPathDirection.FORWARD, 1).getValue(); - String inPort = "1"; - String outPort = "2"; - String tunnelId = "10"; - FlowEntry flowEntry = buildFlowEntry(cookie, inPort, null, outPort, tunnelId, false, null, null); - - RemoveFlow removeFlow = commandBuilder.buildRemoveFlowWithoutMeterFromFlowEntry(SWITCH_ID_A, flowEntry); - assertEquals(cookie, removeFlow.getCookie()); - - DeleteRulesCriteria criteria = removeFlow.getCriteria(); - assertEquals(cookie, criteria.getCookie()); - assertEquals(Integer.valueOf(inPort), criteria.getInPort()); - assertEquals(Integer.valueOf(tunnelId), criteria.getEncapsulationId()); - assertEquals(Integer.valueOf(outPort), criteria.getOutPort()); - assertNull(criteria.getMetadataValue()); - assertNull(criteria.getMetadataMask()); - } - - @Test - public void reinstallDefaultRuleTest() { - List commands = commandBuilder.buildCommandsToReinstallRules( - SWITCH_ID_A, Lists.newArrayList(ROUND_TRIP_LATENCY_RULE_COOKIE)); - - assertEquals(1, commands.size()); - assertEquals(ROUND_TRIP_LATENCY_RULE_COOKIE, commands.get(0).getCookie()); - assertEquals(SWITCH_ID_A, commands.get(0).getSwitchId()); - } - - @Test - public void reinstallServer42RuleTest() { - List commands = commandBuilder.buildCommandsToReinstallRules( - SWITCH_ID_A, Lists.newArrayList(Cookie.SERVER_42_FLOW_RTT_OUTPUT_VLAN_COOKIE)); - - assertEquals(1, commands.size()); - assertEquals(ReinstallServer42FlowForSwitchManagerRequest.class, commands.get(0).getClass()); - ReinstallServer42FlowForSwitchManagerRequest server42Command = - (ReinstallServer42FlowForSwitchManagerRequest) commands.get(0); - - assertEquals(SERVER42_MAC_ADDRESS, server42Command.getServer42MacAddress()); - assertEquals(SERVER42_PORT, server42Command.getServer42Port()); - assertEquals(SERVER42_VLAN, server42Command.getServer42Vlan()); - } - - private FlowEntry buildFlowEntry(Long cookie, String inPort, String inVlan, String outPort, - String tunnelId, boolean tunnelIdIngressRule, String metadataValue, - String metadataMask) { - return FlowEntry.builder() - .cookie(cookie) - .match(FlowMatchField.builder() - .inPort(inPort) - .vlanVid(inVlan) - .metadataValue(metadataValue) - .metadataMask(metadataMask) - .tunnelId(!tunnelIdIngressRule ? tunnelId : null) - .build()) - .instructions(FlowInstructions.builder() - .applyActions(FlowApplyActions.builder() - .flowOutput(outPort) - .pushVxlan(tunnelIdIngressRule ? tunnelId : null) - .build()) - .build()) - .build(); - } -} diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudSpec.groovy index f5b183e7a72..568624b65f3 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/FlowCrudSpec.groovy @@ -7,6 +7,7 @@ import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE import static org.openkilda.functionaltests.extension.tags.Tag.SMOKE_SWITCHES import static org.openkilda.functionaltests.extension.tags.Tag.TOPOLOGY_DEPENDENT import static org.openkilda.functionaltests.helpers.Wrappers.wait +import static org.openkilda.functionaltests.helpers.Wrappers.timedLoop import static org.openkilda.messaging.info.event.IslChangeType.DISCOVERED import static org.openkilda.messaging.info.event.IslChangeType.FAILED import static org.openkilda.messaging.info.event.IslChangeType.MOVED @@ -22,10 +23,8 @@ import org.openkilda.functionaltests.extension.tags.IterationTag import org.openkilda.functionaltests.extension.tags.IterationTags import org.openkilda.functionaltests.extension.tags.Tags import org.openkilda.functionaltests.helpers.PathHelper -import org.openkilda.functionaltests.helpers.Wrappers import org.openkilda.functionaltests.helpers.model.SwitchPair import org.openkilda.messaging.error.MessageError -import org.openkilda.messaging.info.event.IslChangeType import org.openkilda.messaging.info.event.PathNode import org.openkilda.messaging.payload.flow.DetectConnectedDevicesPayload import org.openkilda.messaging.payload.flow.FlowEndpointPayload @@ -74,14 +73,14 @@ class FlowCrudSpec extends HealthCheckSpecification { @Tags([TOPOLOGY_DEPENDENT]) @IterationTags([ @IterationTag(tags = [SMOKE], iterationNameRegex = /vlan /), - @IterationTag(tags = [SMOKE_SWITCHES], iterationNameRegex = - /random vlans|no vlans|single-switch flow with vlans/), + @IterationTag(tags = [SMOKE_SWITCHES], + iterationNameRegex = /random vlans|no vlans|single-switch flow with vlans/), @IterationTag(tags = [LOW_PRIORITY], iterationNameRegex = /and vlan only on/) ]) def "Valid #data.description has traffic and no rule discrepancies [#srcDstStr]"() { given: "A flow" assumeTrue(topology.activeTraffGens.size() >= 2, -"There should be at least two active traffgens for test execution") + "There should be at least two active traffgens for test execution") def traffExam = traffExamProvider.get() def allLinksInfoBefore = northbound.getAllLinks().collectEntries { [it.id, it.availableBandwidth] }.sort() flowHelperV2.addFlow(flow) @@ -129,13 +128,13 @@ class FlowCrudSpec extends HealthCheckSpecification { def flowIsDeleted = true and: "ISL bandwidth is restored" - Wrappers.wait(WAIT_OFFSET) { + wait(WAIT_OFFSET) { def allLinksInfoAfter = northbound.getAllLinks().collectEntries { [it.id, it.availableBandwidth] }.sort() assert allLinksInfoBefore == allLinksInfoAfter } and: "No rule discrepancies on every switch of the flow" - switches.each { sw -> Wrappers.wait(WAIT_OFFSET) { verifySwitchRules(sw.dpId) } } + switches.each { sw -> wait(WAIT_OFFSET) { verifySwitchRules(sw.dpId) } } cleanup: !flowIsDeleted && flow && flowHelperV2.deleteFlow(flow.flowId) @@ -159,10 +158,10 @@ class FlowCrudSpec extends HealthCheckSpecification { Tuple2 flows = data.getNotConflictingFlows() when: "Create the first flow" - flowHelperV2.addFlow(flows.first) + flowHelperV2.addFlow(flows.v1) and: "Try creating a second flow with #data.description" - flowHelperV2.addFlow(flows.second) + flowHelperV2.addFlow(flows.v2) then: "Both flows are successfully created" northboundV2.getAllFlows()*.flowId.containsAll(flows*.flowId) @@ -259,7 +258,7 @@ class FlowCrudSpec extends HealthCheckSpecification { it.destination.vlanId = 0 it.destination.portNumber = flow1.destination.portNumber } - return new Tuple2(flow1, flow2) + return new Tuple2(flow1, flow2) } ], [ @@ -271,7 +270,7 @@ class FlowCrudSpec extends HealthCheckSpecification { it.source.vlanId = 0 it.source.portNumber = flow1.source.portNumber } - return new Tuple2(flow1, flow2) + return new Tuple2(flow1, flow2) } ], [ @@ -284,7 +283,7 @@ class FlowCrudSpec extends HealthCheckSpecification { def flow2 = getFlowHelperV2().randomFlow(srcSwitch, dstSwitch).tap { it.destination.portNumber = flow1.destination.portNumber } - return new Tuple2(flow1, flow2) + return new Tuple2(flow1, flow2) } ], [ @@ -297,7 +296,7 @@ class FlowCrudSpec extends HealthCheckSpecification { def flow2 = getFlowHelperV2().randomFlow(srcSwitch, dstSwitch).tap { it.source.portNumber = flow1.source.portNumber } - return new Tuple2(flow1, flow2) + return new Tuple2(flow1, flow2) } ], [ @@ -311,7 +310,7 @@ class FlowCrudSpec extends HealthCheckSpecification { it.destination.vlanId = 0 it.destination.portNumber = flow1.destination.portNumber } - return new Tuple2(flow1, flow2) + return new Tuple2(flow1, flow2) } ], [ @@ -326,7 +325,7 @@ class FlowCrudSpec extends HealthCheckSpecification { it.source.portNumber = flow1.source.portNumber it.destination.portNumber = flow1.destination.portNumber } - return new Tuple2(flow1, flow2) + return new Tuple2(flow1, flow2) } ] ] @@ -353,7 +352,7 @@ class FlowCrudSpec extends HealthCheckSpecification { def flowIsDeleted = true and: "No rule discrepancies on the switch after delete" - Wrappers.wait(WAIT_OFFSET) { verifySwitchRules(flow.source.switchId) } + wait(WAIT_OFFSET) { verifySwitchRules(flow.source.switchId) } cleanup: !flowIsDeleted && flowHelperV2.deleteFlow(flow.flowId) @@ -449,8 +448,8 @@ class FlowCrudSpec extends HealthCheckSpecification { possibleFlowPaths.size() > 1 && possibleFlowPaths.max { it.size() }.size() > pathNodeCount }.collect { [it.srcSwitch, it.dstSwitch] - }.flatten() ?: assumeTrue(false, "No suiting active neighboring switches with two possible flow paths at least and " + - "different number of hops found") + }.flatten() ?: assumeTrue(false, "No suiting active neighboring switches " + + "with two possible flow paths at least and different number of hops found") and: "Make all shorter forward paths not preferable. Shorter reverse paths are still preferable" possibleFlowPaths.findAll { it.size() == pathNodeCount }.each { @@ -484,10 +483,10 @@ class FlowCrudSpec extends HealthCheckSpecification { antiflap.portDown(isolatedSwitch.dpId, port) } //wait until ISLs are actually got failed - Wrappers.wait(WAIT_OFFSET) { + wait(WAIT_OFFSET) { def islData = northbound.getAllLinks() topology.getRelatedIsls(isolatedSwitch).each { - assert islUtils.getIslInfo(islData, it).get().state == IslChangeType.FAILED + assert islUtils.getIslInfo(islData, it).get().state == FAILED } } @@ -519,8 +518,8 @@ class FlowCrudSpec extends HealthCheckSpecification { isolatedSwitchType: "source", getFlow : { Switch theSwitch -> getFlowHelperV2().randomFlow(getTopologyHelper().getAllNotNeighboringSwitchPairs() - .collectMany { [it, it.reversed] }.find { - it.src == theSwitch + .collectMany { [it, it.reversed] }.find { + it.src == theSwitch }) } ], @@ -528,8 +527,8 @@ class FlowCrudSpec extends HealthCheckSpecification { isolatedSwitchType: "destination", getFlow : { Switch theSwitch -> getFlowHelperV2().randomFlow(getTopologyHelper().getAllNotNeighboringSwitchPairs() - .collectMany { [it, it.reversed] }.find { - it.dst == theSwitch + .collectMany { [it, it.reversed] }.find { + it.dst == theSwitch }) } ] @@ -558,7 +557,7 @@ class FlowCrudSpec extends HealthCheckSpecification { northboundV2.getAllFlows()*.flowId.contains(flow.flowId) and: "Flow eventually gets into UP state" - Wrappers.wait(WAIT_OFFSET) { + wait(WAIT_OFFSET) { assert northboundV2.getFlowStatus(flow.flowId).status == FlowState.UP } @@ -611,7 +610,8 @@ class FlowCrudSpec extends HealthCheckSpecification { when: "Try to update the flow (faked encapsulation type)" def flowInfo = northboundV2.getFlow(flow.flowId) - northboundV2.updateFlow(flowInfo.flowId, flowHelperV2.toRequest(flowInfo.tap { it.encapsulationType = "fake" })) + northboundV2.updateFlow(flowInfo.flowId, + flowHelperV2.toRequest(flowInfo.tap { it.encapsulationType = "fake" })) then: "Flow is not updated" def exc = thrown(HttpClientErrorException) @@ -707,7 +707,7 @@ class FlowCrudSpec extends HealthCheckSpecification { def newIsl = islUtils.replug(isl, false, notConnectedIsl, true, false) islUtils.waitForIslStatus([isl, isl.reversed], MOVED) - Wrappers.wait(discoveryExhaustedInterval + WAIT_OFFSET) { + wait(discoveryExhaustedInterval + WAIT_OFFSET) { [newIsl, newIsl.reversed].each { assert northbound.getLink(it).state == DISCOVERED } } def islIsMoved = true @@ -730,7 +730,7 @@ class FlowCrudSpec extends HealthCheckSpecification { islUtils.waitForIslStatus([isl, isl.reversed], DISCOVERED) islUtils.waitForIslStatus([newIsl, newIsl.reversed], MOVED) northbound.deleteLink(islUtils.toLinkParameters(newIsl)) - Wrappers.wait(WAIT_OFFSET) { assert !islUtils.getIslInfo(newIsl).isPresent() } + wait(WAIT_OFFSET) { assert !islUtils.getIslInfo(newIsl).isPresent() } } database.resetCosts(topology.isls) } @@ -777,7 +777,7 @@ class FlowCrudSpec extends HealthCheckSpecification { flowInfo.statusDetails and: "Rules for main and protected paths are created" - Wrappers.wait(WAIT_OFFSET) { flowHelper.verifyRulesOnProtectedFlow(flow.flowId) } + wait(WAIT_OFFSET) { flowHelper.verifyRulesOnProtectedFlow(flow.flowId) } and: "Validation of flow must be successful" northbound.validateFlow(flow.flowId).each { direction -> assert direction.discrepancies.empty } @@ -794,7 +794,7 @@ class FlowCrudSpec extends HealthCheckSpecification { !northboundV2.getFlow(flow.flowId).statusDetails and: "Rules for protected path are deleted" - Wrappers.wait(WAIT_OFFSET) { + wait(WAIT_OFFSET) { protectedFlowPath.each { sw -> def rules = northbound.getSwitchRules(sw.switchId).flowEntries.findAll { !new Cookie(it.cookie).serviceFlag @@ -913,7 +913,7 @@ class FlowCrudSpec extends HealthCheckSpecification { then: "Flow status is changed to UP only when all rules are actually installed" northboundV2.getFlowStatus(flow.flowId).status == FlowState.IN_PROGRESS - Wrappers.wait(PATH_INSTALLATION_TIME) { + wait(PATH_INSTALLATION_TIME) { assert northboundV2.getFlowStatus(flow.flowId).status == FlowState.UP } def flowInfo = database.getFlow(flow.flowId) @@ -930,7 +930,7 @@ class FlowCrudSpec extends HealthCheckSpecification { then: "Flow is actually removed from flows dump only after all rules are removed" northboundV2.getFlowStatus(flow.flowId).status == FlowState.IN_PROGRESS - Wrappers.wait(RULES_DELETION_TIME) { + wait(RULES_DELETION_TIME) { assert !northboundV2.getFlowStatus(flow.flowId) } withPool(switches.size()) { @@ -943,7 +943,7 @@ class FlowCrudSpec extends HealthCheckSpecification { } northboundV2.getAllFlows().empty - cleanup: + cleanup: northbound.deleteLinkProps(northbound.getLinkProps(topology.isls)) flow && !deleteResponse && flowHelperV2.deleteFlow(flow.flowId) } @@ -989,7 +989,7 @@ class FlowCrudSpec extends HealthCheckSpecification { and: "Flow rules are recreated" def flowInfoFromDb2 = database.getFlow(flow.flowId) - Wrappers.wait(RULES_INSTALLATION_TIME) { + wait(RULES_INSTALLATION_TIME) { def isMultiTableEnabled = northbound.getSwitchProperties(srcSwitch.dpId).multiTable with(northbound.getSwitchRules(srcSwitch.dpId).flowEntries.findAll { !new Cookie(it.cookie).serviceFlag @@ -1041,7 +1041,7 @@ class FlowCrudSpec extends HealthCheckSpecification { and: "Flow rules are removed from the old dst switch" def flowInfoFromDb3 = database.getFlow(flow.flowId) - Wrappers.wait(RULES_DELETION_TIME) { + wait(RULES_DELETION_TIME) { with(northbound.getSwitchRules(dstSwitch.dpId).flowEntries.findAll { !new Cookie(it.cookie).serviceFlag }) { rules -> @@ -1052,7 +1052,7 @@ class FlowCrudSpec extends HealthCheckSpecification { } and: "Flow rules are installed on the new dst switch" - Wrappers.wait(RULES_INSTALLATION_TIME) { + wait(RULES_INSTALLATION_TIME) { with(northbound.getSwitchRules(newDstSwitch.dpId).flowEntries.findAll { !new Cookie(it.cookie).serviceFlag }) { rules -> @@ -1070,7 +1070,7 @@ class FlowCrudSpec extends HealthCheckSpecification { } and: "The new and old dst switches pass switch validation" - Wrappers.wait(RULES_DELETION_TIME) { + wait(RULES_DELETION_TIME) { [dstSwitch, newDstSwitch]*.dpId.each { switchId -> with(northbound.validateSwitch(switchId)) { validation -> validation.verifyRuleSectionsAreEmpty(["missing", "excess", "misconfigured"]) @@ -1106,7 +1106,7 @@ class FlowCrudSpec extends HealthCheckSpecification { then: "Flow is rerouted" def newCurrentPath - Wrappers.wait(rerouteDelay + WAIT_OFFSET) { + wait(rerouteDelay + WAIT_OFFSET) { newCurrentPath = pathHelper.convert(northbound.getFlowPath(flow.flowId)) assert newCurrentPath != currentPath } @@ -1163,7 +1163,7 @@ class FlowCrudSpec extends HealthCheckSpecification { } and: "Flow path is not rebuild" - Wrappers.timedLoop(rerouteDelay) { + timedLoop(rerouteDelay) { assert pathHelper.convert(northbound.getFlowPath(flow.flowId)) == currentPath } @@ -1180,7 +1180,7 @@ class FlowCrudSpec extends HealthCheckSpecification { } and: "Flow path is not rebuild" - Wrappers.timedLoop(rerouteDelay) { + timedLoop(rerouteDelay) { assert pathHelper.convert(northbound.getFlowPath(flow.flowId)) == currentPath } @@ -1202,7 +1202,7 @@ class FlowCrudSpec extends HealthCheckSpecification { } and: "Flow path is not rebuild" - Wrappers.timedLoop(rerouteDelay + WAIT_OFFSET / 2) { + timedLoop(rerouteDelay + WAIT_OFFSET / 2) { assert pathHelper.convert(northbound.getFlowPath(flow.flowId)) == currentPath } @@ -1241,11 +1241,11 @@ class FlowCrudSpec extends HealthCheckSpecification { @Tags([TOPOLOGY_DEPENDENT, LOW_PRIORITY]) def "System allows to update single switch flow to multi switch flow"() { given: "A single switch flow with enabled lldp/arp on the dst side" + assumeTrue(useMultitable, "This test can be run in multiTable mode due to lldp/arp") def swPair = topologyHelper.getNeighboringSwitchPair() def flow = flowHelperV2.singleSwitchFlow(swPair.src) - //https://github.com/telstra/open-kilda/issues/4607 -// flow.destination.detectConnectedDevices.lldp = true -// flow.destination.detectConnectedDevices.arp = true + flow.destination.detectConnectedDevices.lldp = true + flow.destination.detectConnectedDevices.arp = true flowHelperV2.addFlow(flow) when: "Update the dst endpoint to make this flow as multi switch flow" @@ -1311,7 +1311,7 @@ class FlowCrudSpec extends HealthCheckSpecification { "switchId=\"${conflictingFlow."$conflictingEndpoint".switchId}\" " + "port=${conflictingFlow."$conflictingEndpoint".portNumber}" if (0 < conflictingFlow."$conflictingEndpoint".vlanId) { - message += " vlanId=${conflictingFlow."$conflictingEndpoint".vlanId}"; + message += " vlanId=${conflictingFlow."$conflictingEndpoint".vlanId}" } message += ", existing flow '$flow.flowId' $endpoint: " + "switchId=\"${flow."$endpoint".switchId}\" " + @@ -1400,36 +1400,32 @@ class FlowCrudSpec extends HealthCheckSpecification { .sort { sw -> topology.activeTraffGens.findAll { it.switchConnected == sw }.size() }.reverse() .unique { it.description } .inject([]) { r, sw -> - r << [ - description: "single-switch flow with vlans", - flow : flowHelperV2.singleSwitchFlow(sw) - ] - r << [ - description: "single-switch flow without vlans", - flow : flowHelperV2.singleSwitchFlow(sw).tap { - it.source.vlanId = 0 - it.destination.vlanId = 0 - } - ] - r << [ - description: "single-switch flow with vlan only on dst", - flow : flowHelperV2.singleSwitchFlow(sw).tap { - it.source.vlanId = 0 - } - ] - r - } + r << [ + description: "single-switch flow with vlans", + flow : flowHelperV2.singleSwitchFlow(sw) + ] + r << [ + description: "single-switch flow without vlans", + flow : flowHelperV2.singleSwitchFlow(sw).tap { + it.source.vlanId = 0 + it.destination.vlanId = 0 + } + ] + r << [ + description: "single-switch flow with vlan only on dst", + flow : flowHelperV2.singleSwitchFlow(sw).tap { + it.source.vlanId = 0 + } + ] + r + } } boolean isFlowPingable(FlowRequestV2 flow) { if (flow.source.switchId == flow.destination.switchId) { return false - } else if (topology.find(flow.source.switchId).ofVersion == "OF_12" || - topology.find(flow.destination.switchId).ofVersion == "OF_12") { - return false - } else { - return true - } + } else return !(topology.find(flow.source.switchId).ofVersion == "OF_12" || + topology.find(flow.destination.switchId).ofVersion == "OF_12") } /** diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PartialUpdateSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PartialUpdateSpec.groovy index 716f331f443..fdae5b3f32a 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PartialUpdateSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/flows/PartialUpdateSpec.groovy @@ -5,6 +5,7 @@ import static org.assertj.core.api.Assertions.assertThat import static org.junit.jupiter.api.Assumptions.assumeTrue import static org.openkilda.functionaltests.extension.tags.Tag.LOW_PRIORITY import static org.openkilda.model.cookie.CookieBase.CookieType.SERVICE_OR_FLOW_SEGMENT +import static org.openkilda.testing.Constants.RULES_DELETION_TIME import static org.openkilda.testing.Constants.RULES_INSTALLATION_TIME import static org.openkilda.testing.Constants.WAIT_OFFSET import static spock.util.matcher.HamcrestSupport.expect @@ -364,16 +365,15 @@ class PartialUpdateSpec extends HealthCheckSpecification { it.reverse.pingSuccess } - //issue with excess rules: https://github.com/telstra/open-kilda/issues/4055 -// and: "The new and old dst switches pass switch validation" -// Wrappers.wait(RULES_DELETION_TIME) { -// [dstSwitch, newDstSwitch]*.dpId.each { switchId -> -// with(northbound.validateSwitch(switchId)) { validation -> -// validation.verifyRuleSectionsAreEmpty(["missing", "excess", "misconfigured"]) -// validation.verifyMeterSectionsAreEmpty(["missing", "excess", "misconfigured"]) -// } -// } -// } + and: "The new and old dst switches pass switch validation" + Wrappers.wait(RULES_DELETION_TIME) { + [dstSwitch, newDstSwitch]*.dpId.each { switchId -> + with(northbound.validateSwitch(switchId)) { validation -> + validation.verifyRuleSectionsAreEmpty(["missing", "excess", "misconfigured"]) + validation.verifyMeterSectionsAreEmpty(["missing", "excess", "misconfigured"]) + } + } + } def dstSwitchesAreFine = false cleanup: diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/grpc/LogicalPortSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/grpc/LogicalPortSpec.groovy index b2ff1cf019f..aa01a0c2c43 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/grpc/LogicalPortSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/grpc/LogicalPortSpec.groovy @@ -98,10 +98,9 @@ class LogicalPortSpec extends GrpcBaseSpecification { [logicalPortNumber: 63488, portNumber : false, errorMessage : "Valid logicalportno range is 100 to 63487."], - //https://github.com/telstra/open-kilda/issues/3973 -// [logicalPortNumber: false, -// portNumber : 44444, -// errorMessage : "Invalid portno value."], + [logicalPortNumber: false, + portNumber : 44444, + errorMessage : "Invalid portno value."], ], noviflowSwitches].combinations() } diff --git a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/LagPortSpec.groovy b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/LagPortSpec.groovy index c18e3ecfac6..90df7d70ee7 100644 --- a/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/LagPortSpec.groovy +++ b/src-java/testing/functional-tests/src/test/groovy/org/openkilda/functionaltests/spec/switches/LagPortSpec.groovy @@ -402,9 +402,14 @@ class LagPortSpec extends HealthCheckSpecification { errorMsg : "Physical port number %d intersects with existing ISLs" ], [ - description: "is more than lagOffset", + description: "more than lagOffset", portNumber : { 2008 }, errorMsg : "Physical port number %d can't be greater than LAG port offset $lagOffset." + ], + [ + description: "not exist", + portNumber : { Switch s -> s.maxPort + 1 }, + errorMsg : "Invalid portno value." ] ] }