From 96b896d04336010065b2d740bba8bd6a0187dbd2 Mon Sep 17 00:00:00 2001 From: Sean Condon Date: Mon, 11 Dec 2017 12:44:29 -0800 Subject: [PATCH] [ONOS-7262] Cfm improvements to allow RMeps and Mds and Mas to be added and deleted Change-Id: Ibffb13d046bfb29dbe88de7b558c95fbf9db046d --- .../cfm/cli/CfmMdListMdCommand.java | 85 ++- .../cfm/cli/CfmMepListCommand.java | 173 ++++++ .../cfm/cli/CfmMepListDeviceCommand.java | 77 +++ .../cli/completer/CfmDeviceIdCompleter.java | 52 ++ .../cfm/cli/completer/CfmMepIdCompleter.java | 81 +++ .../cfm/rest/CfmWebApplication.java | 1 + .../cfm/rest/DeviceMepWebResource.java | 69 +++ .../onosproject/cfm/rest/MdWebResource.java | 1 + .../onosproject/cfm/rest/MepWebResource.java | 7 +- .../cfm/web/MaintenanceAssociationCodec.java | 11 +- .../OSGI-INF/blueprint/shell-config.xml | 14 + .../main/resources/definitions/MaCreate.json | 3 +- .../cfm/impl/MepWebResourceTest.java | 4 +- drivers/microsemi/BUCK | 3 +- .../microsemi/EA1000CfmMepProgrammable.java | 487 +++++++++++++---- .../microsemi/yang/MseaCfmNetconfService.java | 63 ++- .../microsemi/yang/impl/MseaCfmManager.java | 206 ++++++- .../microsemi/yang/utils/MaNameUtil.java | 59 +- .../microsemi/yang/utils/MdNameUtil.java | 139 +++++ .../EA1000CfmMepProgrammableTest.java | 140 ++++- .../microsemi/MockEa1000DriverHandler.java | 10 + .../microsemi/yang/MockCfmMdService.java | 24 +- .../microsemi/yang/MockCfmMepService.java | 137 +++++ .../yang/MockNetconfSessionEa1000.java | 100 +++- .../microsemi/yang/MseaCfmManagerTest.java | 176 +++++- .../cfm/DefaultMaintenanceAssociation.java | 6 + .../cfm/MaintenanceAssociation.java | 2 + .../l2monitoring/cfm/identifier/MepKeyId.java | 87 +++ .../cfm/service/CfmMdService.java | 3 +- .../l2monitoring/cfm/service/CfmMepEvent.java | 11 +- .../cfm/service/CfmMepProgrammable.java | 85 ++- .../cfm/service/CfmMepService.java | 74 +-- .../cfm/service/CfmMepServiceBase.java | 95 ++++ .../net/l2monitoring/cfm/service/MdEvent.java | 56 +- .../l2monitoring/cfm/service/MepStore.java | 84 +++ .../cfm/service/MepStoreDelegate.java | 24 + .../l2monitoring/cfm/impl/CfmMdManager.java | 5 +- .../l2monitoring/cfm/impl/CfmMepManager.java | 507 ++++++++++++++---- .../cfm/impl/DistributedMdStore.java | 65 ++- .../cfm/impl/DistributedMepStore.java | 193 +++++++ .../cfm/impl/CfmMdManagerTest.java | 18 +- .../cfm/impl/CfmMepManagerTest.java | 141 ++++- .../cfm/impl/TestCfmMepProgrammable.java | 50 +- 43 files changed, 3213 insertions(+), 415 deletions(-) create mode 100644 apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListCommand.java create mode 100644 apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListDeviceCommand.java create mode 100644 apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmDeviceIdCompleter.java create mode 100644 apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMepIdCompleter.java create mode 100644 apps/cfm/src/main/java/org/onosproject/cfm/rest/DeviceMepWebResource.java create mode 100644 drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MdNameUtil.java create mode 100644 drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMepService.java create mode 100644 incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/identifier/MepKeyId.java create mode 100644 incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepServiceBase.java create mode 100644 incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStore.java create mode 100644 incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStoreDelegate.java create mode 100644 incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMepStore.java diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java index 341bd7dd34..f3c5b6eaf9 100644 --- a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java +++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMdListMdCommand.java @@ -20,6 +20,12 @@ import org.apache.karaf.shell.commands.Command; import org.onosproject.cli.AbstractShellCommand; import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation; import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaId2Octet; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdIccY1731; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdPrimaryVid; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdRfc2685VpnId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName; @@ -45,29 +51,7 @@ public class CfmMdListMdCommand extends AbstractShellCommand { CfmMdService service = get(CfmMdService.class); if (name != null) { - String[] nameParts = name.split("[()]"); - if (nameParts.length != 2) { - throw new IllegalArgumentException("Invalid name format. " + - "Must be in the format of "); - } - - MdId mdId = null; - MdId.MdNameType nameTypeEnum = MdId.MdNameType.valueOf(nameParts[1]); - switch (nameTypeEnum) { - case DOMAINNAME: - mdId = MdIdDomainName.asMdId(nameParts[0]); - break; - case MACANDUINT: - mdId = MdIdMacUint.asMdId(nameParts[0]); - break; - case NONE: - mdId = MdIdNone.asMdId(); - break; - case CHARACTERSTRING: - default: - mdId = MdIdCharStr.asMdId(nameParts[0]); - } - + MdId mdId = parseMdName(name); print("Maintenance Domain:"); Optional md = service.getMaintenanceDomain(mdId); print(printMd(md)); @@ -131,4 +115,59 @@ public class CfmMdListMdCommand extends AbstractShellCommand { return sb.toString(); } + + public static MdId parseMdName(String mdStr) { + String[] nameParts = mdStr.split("[()]"); + if (nameParts.length != 2) { + throw new IllegalArgumentException("Invalid name format. " + + "Must be in the format of "); + } + + MdId mdId = null; + MdId.MdNameType nameTypeEnum = MdId.MdNameType.valueOf(nameParts[1]); + switch (nameTypeEnum) { + case DOMAINNAME: + mdId = MdIdDomainName.asMdId(nameParts[0]); + break; + case MACANDUINT: + mdId = MdIdMacUint.asMdId(nameParts[0]); + break; + case NONE: + mdId = MdIdNone.asMdId(); + break; + case CHARACTERSTRING: + default: + mdId = MdIdCharStr.asMdId(nameParts[0]); + } + return mdId; + } + + public static MaIdShort parseMaName(String maStr) { + String[] nameParts = maStr.split("[()]"); + if (nameParts.length != 2) { + throw new IllegalArgumentException("Invalid name format. " + + "Must be in the format of "); + } + + MaIdShort maId = null; + MaIdShort.MaIdType nameTypeEnum = MaIdShort.MaIdType.valueOf(nameParts[1]); + switch (nameTypeEnum) { + case ICCY1731: + maId = MaIdIccY1731.asMaId(nameParts[0]); + break; + case PRIMARYVID: + maId = MaIdPrimaryVid.asMaId(nameParts[0]); + break; + case RFC2685VPNID: + maId = MaIdRfc2685VpnId.asMaIdHex(nameParts[0]); + break; + case TWOOCTET: + maId = MaId2Octet.asMaId(nameParts[0]); + case CHARACTERSTRING: + default: + maId = MaIdCharStr.asMaId(nameParts[0]); + } + return maId; + } + } diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListCommand.java new file mode 100644 index 0000000000..b406947b4c --- /dev/null +++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListCommand.java @@ -0,0 +1,173 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * 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.onosproject.cfm.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; +import org.slf4j.Logger; + +import static org.onosproject.cfm.cli.CfmMdListMdCommand.parseMaName; +import static org.onosproject.cfm.cli.CfmMdListMdCommand.parseMdName; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Lists a particular Maintenance Domain. + */ +@Command(scope = "onos", name = "cfm-mep-list", + description = "Lists a filtered set of MEPs or all if no parameters specified.") +public class CfmMepListCommand extends AbstractShellCommand { + private final Logger log = getLogger(getClass()); + @Argument(index = 0, name = "md", + description = "Maintenance Domain name and type (in brackets) - will use all MDs if not specified", + required = false, multiValued = false) + String mdStr = null; + @Argument(index = 1, name = "ma", + description = "Maintenance Association name and type (in brackets) - requires MD", + required = false, multiValued = false) + String maStr = null; + @Argument(index = 2, name = "mep", + description = "MEP identifier - requires MD and MA", + required = false, multiValued = false) + String mepStr = null; + + @Override + protected void execute() { + CfmMepService mepService = get(CfmMepService.class); + CfmMdService mdService = get(CfmMdService.class); + + if (mdStr != null && !mdStr.isEmpty()) { + + + MdId mdId = parseMdName(mdStr); + print(printMdId(mdId)); + + if (maStr != null && !maStr.isEmpty()) { + MaIdShort maId = parseMaName(maStr); + print(printMaId(maId)); + + if (mepStr != null && !mepStr.isEmpty()) { + MepId mepId = MepId.valueOf(Short.parseShort(mepStr)); + try { + MepEntry mep = mepService.getMep(mdId, maId, mepId); + if (mep != null) { + print(printMepEntry(mep)); + } + } catch (CfmConfigException e) { + log.error("Error retrieving Mep details {}", + new MepKeyId(mdId, maId, mepId), e); + } + + //MD, MA and MEP given + } else { + //MD and MA given but no MEP given + try { + mepService.getAllMeps(mdId, maId).forEach(mep -> { + print(printMepEntry(mep)); + }); + } catch (CfmConfigException e) { + log.error("Error retrieving Meps for {}/{}", + mdId.mdName(), maId.maName(), e); + } + } + } else { + //MD given but no MA given + mdService.getAllMaintenanceAssociation(mdId).forEach(ma -> { + print(printMaId(ma.maId())); + try { + mepService.getAllMeps(mdId, ma.maId()).forEach(mep -> { + print(printMepEntry(mep)); + }); + } catch (CfmConfigException e) { + log.error("Error retrieving Meps for {}/{}", + mdId.mdName(), ma.maId().maName(), e); + } + }); + + } + } else { + mdService.getAllMaintenanceDomain().forEach(md -> { + print(printMdId(md.mdId())); + + mdService.getAllMaintenanceAssociation(md.mdId()).forEach(ma -> { + print(printMaId(ma.maId())); + try { + mepService.getAllMeps(md.mdId(), ma.maId()).forEach(mep -> { + print(printMepEntry(mep)); + }); + } catch (CfmConfigException e) { + log.error("Error retrieving Meps for {}/{}", + md.mdId().mdName(), ma.maId().maName(), e); + } + }); + }); + } + } + + /** + * Print the whole MEP Entry (config and status). + * @param mep The MEPEntry to print + * @return A string with MepEntry details + */ + public static String printMepEntry(MepEntry mep) { + StringBuffer sb = new StringBuffer("MEP: "); + sb.append(mep.mepId()); + sb.append(" Device:" + mep.deviceId()); + sb.append(", Port: " + mep.port()); + sb.append(", Vlan: " + mep.primaryVid()); + sb.append(", AdminSt: " + mep.administrativeState()); + sb.append(", CciEnabled: " + mep.cciEnabled()); + sb.append(", Priority: " + mep.ccmLtmPriority()); + sb.append("\n"); //The following are state + sb.append(", Total CCMs: " + mep.totalCcmsTransmitted()); + sb.append(", MAC: " + mep.macAddress()); + sb.append(", Fault: " + mep.fngState()); + + mep.activeRemoteMepList().forEach(rmep -> { + sb.append("\n\tRmep: " + rmep.remoteMepId()); + sb.append(", Mac: " + rmep.macAddress()); + sb.append(", State: " + rmep.state()); + sb.append(", Failed Time: " + rmep.failedOrOkTime()); + + }); + + + return sb.toString(); + } + + public static String printMdId(MdId mdId) { + StringBuffer sb = new StringBuffer("MD: "); + sb.append(mdId.mdName()); + sb.append("(" + mdId.nameType() + ")"); + return sb.toString(); + } + + public static String printMaId(MaIdShort maId) { + StringBuffer sb = new StringBuffer("MA: "); + sb.append(maId.maName()); + sb.append("(" + maId.nameType() + ")"); + return sb.toString(); + } + +} diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListDeviceCommand.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListDeviceCommand.java new file mode 100644 index 0000000000..c99399a09b --- /dev/null +++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/CfmMepListDeviceCommand.java @@ -0,0 +1,77 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * 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.onosproject.cfm.cli; + +import org.apache.karaf.shell.commands.Argument; +import org.apache.karaf.shell.commands.Command; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.incubator.net.l2monitoring.cfm.Mep; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; +import org.onosproject.net.DeviceId; +import org.slf4j.Logger; + +import static org.slf4j.LoggerFactory.getLogger; + +/** + * Lists all the MEPs on a particular device. + */ +@Command(scope = "onos", name = "cfm-mep-device-list", + description = "Lists a set of MEPs filtered by device.") +public class CfmMepListDeviceCommand extends AbstractShellCommand { + private final Logger log = getLogger(getClass()); + @Argument(index = 0, name = "device", + description = "Device Id", + required = true, multiValued = false) + String deviceStr = null; + + @Override + protected void execute() { + CfmMepService mepService = get(CfmMepService.class); + if (deviceStr != null) { + DeviceId deviceId = DeviceId.deviceId(deviceStr); + try { + mepService.getAllMepsByDevice(deviceId).forEach(mep -> { + print(printMep(mep)); + }); + } catch (CfmConfigException e) { + log.error("Error retrieving Meps for Device {}", + deviceId, e); + } + } + } + + /** + * Print only the config part of the MEP. + * @param mep The MEP to print + * @return A string with MD name, MA name and Mep details + */ + public static String printMep(Mep mep) { + StringBuffer sb = new StringBuffer("MEP: "); + sb.append(mep.mdId().mdName() + "/"); + sb.append(mep.maId().maName() + "/"); + sb.append(mep.mepId()); + sb.append(" Device:" + mep.deviceId()); + sb.append(", Port: " + mep.port()); + sb.append(", Vlan: " + mep.primaryVid()); + sb.append(", AdminSt: " + mep.administrativeState()); + sb.append(", CciEnabled: " + mep.cciEnabled()); + sb.append(", Priority: " + mep.ccmLtmPriority()); + + return sb.toString(); + } + +} diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmDeviceIdCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmDeviceIdCompleter.java new file mode 100644 index 0000000000..6e2e29fad8 --- /dev/null +++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmDeviceIdCompleter.java @@ -0,0 +1,52 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * 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.onosproject.cfm.cli.completer; + +import org.apache.karaf.shell.console.Completer; +import org.apache.karaf.shell.console.completer.StringsCompleter; +import org.onosproject.cli.AbstractShellCommand; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable; +import org.onosproject.net.Device; +import org.onosproject.net.device.DeviceService; + +import java.util.Iterator; +import java.util.List; +import java.util.SortedSet; + +/** + * CLI completer for Devices that support Meps. + */ +public class CfmDeviceIdCompleter implements Completer { + @Override + public int complete(String buffer, int cursor, List candidates) { + // Delegate string completer + StringsCompleter delegate = new StringsCompleter(); + + // Fetch our service and feed it's offerings to the string completer + DeviceService service = AbstractShellCommand.get(DeviceService.class); + Iterator it = service.getDevices().iterator(); + SortedSet strings = delegate.getStrings(); + while (it.hasNext()) { + Device device = it.next(); + if (device.is(CfmMepProgrammable.class)) { + strings.add(device.id().toString()); + } + } + + // Now let the completer do the work for figuring out what to offer. + return delegate.complete(buffer, cursor, candidates); + } +} diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMepIdCompleter.java b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMepIdCompleter.java new file mode 100644 index 0000000000..60199161ea --- /dev/null +++ b/apps/cfm/src/main/java/org/onosproject/cfm/cli/completer/CfmMepIdCompleter.java @@ -0,0 +1,81 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * 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.onosproject.cfm.cli.completer; + +import org.onosproject.cli.AbstractChoicesCompleter; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; + +import static org.onosproject.cli.AbstractShellCommand.get; +import static org.slf4j.LoggerFactory.getLogger; + +/** + * CLI completer for Mep Id creation. + */ +public class CfmMepIdCompleter extends AbstractChoicesCompleter { + private final Logger log = getLogger(getClass()); + + @Override + public List choices() { + List choices = new ArrayList<>(); + + CfmMdService mdService = get(CfmMdService.class); + CfmMepService mepService = get(CfmMepService.class); + + mdService.getAllMaintenanceDomain().forEach(md -> { + choices.add(new StringBuilder(md.mdId().mdName()) + .append("(") + .append(md.mdId().nameType()) + .append(")").toString()); + + md.maintenanceAssociationList().forEach(ma -> { + choices.add(new StringBuilder(md.mdId().mdName()) + .append("(") + .append(md.mdId().nameType()) + .append(") ") + .append(ma.maId().maName()) + .append("(") + .append(ma.maId().nameType()) + .append(")").toString()); + + try { + mepService.getAllMeps(md.mdId(), ma.maId()).forEach(mep -> + choices.add(new StringBuilder(md.mdId().mdName()) + .append("(") + .append(md.mdId().nameType()) + .append(") ") + .append(ma.maId().maName()) + .append("(") + .append(ma.maId().nameType()) + .append(") ") + .append(mep.mepId()).toString()) + ); + } catch (CfmConfigException e) { + log.warn("Unable to retrieve mep details", e); + } + } + ); + }); + + return choices; + } + +} diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/rest/CfmWebApplication.java b/apps/cfm/src/main/java/org/onosproject/cfm/rest/CfmWebApplication.java index 13157f2e9f..b32174ec66 100644 --- a/apps/cfm/src/main/java/org/onosproject/cfm/rest/CfmWebApplication.java +++ b/apps/cfm/src/main/java/org/onosproject/cfm/rest/CfmWebApplication.java @@ -32,6 +32,7 @@ public class CfmWebApplication extends AbstractWebApplication { MdWebResource.class, MaWebResource.class, MepWebResource.class, + DeviceMepWebResource.class, DmWebResource.class, LmWebResource.class); } diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/rest/DeviceMepWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/rest/DeviceMepWebResource.java new file mode 100644 index 0000000000..5fbed6d741 --- /dev/null +++ b/apps/cfm/src/main/java/org/onosproject/cfm/rest/DeviceMepWebResource.java @@ -0,0 +1,69 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.onosproject.cfm.rest; + +import com.fasterxml.jackson.databind.node.ArrayNode; +import org.onosproject.incubator.net.l2monitoring.cfm.Mep; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; +import org.onosproject.net.DeviceId; +import org.onosproject.rest.AbstractWebResource; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import javax.ws.rs.Consumes; +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import java.util.Collection; + +/** + * Layer 2 CFM Maintenance Association Endpoint (MEP) by Device web resource. + */ +@Path("device") +public class DeviceMepWebResource extends AbstractWebResource { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + /** + * Get all MEPs by Device Id. The device should support Meps + * + * @param deviceId The id of a device. + * @return 200 OK with a list of MEPS or 500 on error + */ + @GET + @Path("{device_id}") + @Produces(MediaType.APPLICATION_JSON) + @Consumes(MediaType.APPLICATION_JSON) + public Response getAllMepsForDevice(@PathParam("device_id") String deviceId) { + DeviceId deviceIdObj = DeviceId.deviceId(deviceId); + log.debug("GET all Meps called for Device {}", deviceIdObj); + try { + Collection mepCollection = get(CfmMepService.class) + .getAllMepsByDevice(deviceIdObj); + ArrayNode an = mapper().createArrayNode(); + an.add(codec(Mep.class).encode(mepCollection, this)); + return ok(mapper().createObjectNode().set("meps", an)).build(); + } catch (CfmConfigException e) { + log.error("Get all Meps on device {} failed because of exception", + deviceIdObj, e); + return Response.serverError().entity("{ \"failure\":\"" + e.toString() + "\" }").build(); + } + } +} diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/rest/MdWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/rest/MdWebResource.java index c642f4ae0e..0e523a7092 100644 --- a/apps/cfm/src/main/java/org/onosproject/cfm/rest/MdWebResource.java +++ b/apps/cfm/src/main/java/org/onosproject/cfm/rest/MdWebResource.java @@ -82,6 +82,7 @@ public class MdWebResource extends AbstractWebResource { log.debug("GET called for MD {}", mdName); try { MaintenanceDomain md = get(CfmMdService.class) + //FIXME Handle other types of name constructs e.g. DomainName .getMaintenanceDomain(MdIdCharStr.asMdId(mdName)) .orElseThrow(() -> new IllegalArgumentException( "MD " + mdName + " not Found")); diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/rest/MepWebResource.java b/apps/cfm/src/main/java/org/onosproject/cfm/rest/MepWebResource.java index fa8c7287a0..52d4150e23 100644 --- a/apps/cfm/src/main/java/org/onosproject/cfm/rest/MepWebResource.java +++ b/apps/cfm/src/main/java/org/onosproject/cfm/rest/MepWebResource.java @@ -18,6 +18,7 @@ package org.onosproject.cfm.rest; import java.io.InputStream; import java.net.URI; import java.util.Collection; +import java.util.Optional; import javax.ws.rs.Consumes; import javax.ws.rs.DELETE; @@ -145,7 +146,7 @@ public class MepWebResource extends AbstractWebResource { MdId mdId = MdIdCharStr.asMdId(mdName); MaIdShort maId = MaIdCharStr.asMaId(maName); boolean deleted = get(CfmMepService.class) - .deleteMep(mdId, maId, MepId.valueOf(mepIdShort)); + .deleteMep(mdId, maId, MepId.valueOf(mepIdShort), Optional.empty()); if (!deleted) { return Response.notModified(mdName + "/" + maName + "/" + mepIdShort + " did not exist").build(); @@ -187,8 +188,8 @@ public class MepWebResource extends AbstractWebResource { Mep mep = ((MepCodec) mepCodec).decode((ObjectNode) cfg, this, mdName, maName); - Boolean issuccess = get(CfmMepService.class).createMep(mdId, maId, mep); - if (!issuccess) { + Boolean didNotExist = get(CfmMepService.class).createMep(mdId, maId, mep); + if (!didNotExist) { return Response.notModified(mdName + "/" + ma.maId() + "/" + mep.mepId() + " already exists").build(); } diff --git a/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java b/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java index 62be35d03e..6c3829fbc1 100644 --- a/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java +++ b/apps/cfm/src/main/java/org/onosproject/cfm/web/MaintenanceAssociationCodec.java @@ -124,10 +124,13 @@ public class MaintenanceAssociationCodec extends JsonCodec remoteMeps = (new RMepCodec()).decode( - (ArrayNode) nullIsIllegal(maNode.get(RMEP_LIST), "rmep-list is required"), context); - for (MepId remoteMep:remoteMeps) { - builder = builder.addToRemoteMepIdList(remoteMep); + JsonNode rmepListJson = maNode.get(RMEP_LIST); + if (rmepListJson != null) { + List remoteMeps = (new RMepCodec()).decode( + (ArrayNode) rmepListJson, context); + for (MepId remoteMep:remoteMeps) { + builder = builder.addToRemoteMepIdList(remoteMep); + } } return builder.build(); diff --git a/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml b/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml index 5f53ebab75..fcd66a0c6c 100644 --- a/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml +++ b/apps/cfm/src/main/resources/OSGI-INF/blueprint/shell-config.xml @@ -57,6 +57,18 @@ + + + + + + + + + + + + @@ -68,6 +80,8 @@ + + diff --git a/apps/cfm/src/main/resources/definitions/MaCreate.json b/apps/cfm/src/main/resources/definitions/MaCreate.json index fea36dbbd5..5b6be27b36 100644 --- a/apps/cfm/src/main/resources/definitions/MaCreate.json +++ b/apps/cfm/src/main/resources/definitions/MaCreate.json @@ -133,7 +133,8 @@ } } }, - "example": [{"rmep": 10}, {"rmep": 20}] + "example": [{"rmep": 10}, {"rmep": 20}], + "description": "An optional set of Mep IDs that might be on equipment not managed by ONOS" } } } diff --git a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java index fde5428085..6a5dbca6b5 100644 --- a/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java +++ b/apps/cfm/src/test/java/org/onosproject/cfm/impl/MepWebResourceTest.java @@ -185,7 +185,7 @@ public class MepWebResourceTest extends CfmResourceTest { @Test public void testDeleteMepValid() throws CfmConfigException { - expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 1))) + expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 1), Optional.empty())) .andReturn(true).anyTimes(); replay(mepService); @@ -199,7 +199,7 @@ public class MepWebResourceTest extends CfmResourceTest { @Test public void testDeleteMepNotFound() throws CfmConfigException { - expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 2))) + expect(mepService.deleteMep(MDNAME1, MANAME1, MepId.valueOf((short) 2), Optional.empty())) .andReturn(false).anyTimes(); replay(mepService); diff --git a/drivers/microsemi/BUCK b/drivers/microsemi/BUCK index 80ef2c81eb..210d76cc3d 100644 --- a/drivers/microsemi/BUCK +++ b/drivers/microsemi/BUCK @@ -15,7 +15,8 @@ TEST_DEPS = [ '//core/api:onos-api-tests', '//drivers/netconf:onos-drivers-netconf-tests', '//utils/osgi:onlab-osgi-tests', - '//incubator/net:onos-incubator-net' + '//incubator/net:onos-incubator-net', + '//incubator/net:onos-incubator-net-tests' ] APPS = [ diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammable.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammable.java index 143ee79b09..a5ff236acc 100755 --- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammable.java +++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammable.java @@ -16,12 +16,17 @@ package org.onosproject.drivers.microsemi; import static com.google.common.base.Preconditions.checkNotNull; +import static org.onosproject.drivers.microsemi.yang.utils.MaNameUtil.getApiMaIdFromYangMaName; +import static org.onosproject.drivers.microsemi.yang.utils.MdNameUtil.getApiMdIdFromYangMdName; +import static org.onosproject.drivers.microsemi.yang.utils.MdNameUtil.getYangMdNameFromApiMdId; import static org.slf4j.LoggerFactory.getLogger; import java.time.Duration; +import java.util.ArrayList; import java.util.Collection; +import java.util.NoSuchElementException; +import java.util.Optional; -import org.onlab.packet.IpAddress; import org.onlab.packet.MacAddress; import org.onlab.packet.VlanId; import org.onlab.util.HexString; @@ -47,14 +52,12 @@ import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.RemoteMepEn import org.onosproject.incubator.net.l2monitoring.cfm.RemoteMepEntry.RemoteMepState; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; -import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; -import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName; -import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint; -import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; import org.onosproject.net.DeviceId; import org.onosproject.net.PortNumber; import org.onosproject.net.driver.AbstractHandlerBehaviour; @@ -62,7 +65,6 @@ import org.onosproject.netconf.DatastoreId; import org.onosproject.netconf.NetconfController; import org.onosproject.netconf.NetconfException; import org.onosproject.netconf.NetconfSession; -import org.onosproject.yang.gen.v1.ietfinettypes.rev20130715.ietfinettypes.DomainName; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.MseaCfm; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.MseaCfmOpParam; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.DefaultMefCfm; @@ -83,11 +85,6 @@ import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenanc import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.ContinuityCheck; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.DefaultContinuityCheck; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.InterfaceEnum; -import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultMacAddressAndUint; -import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameCharacterString; -import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameDomainName; -import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameNone; -import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.namedomainname.NameDomainNameUnion; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.remotemepstatetype.RemoteMepStateTypeEnum; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.targetaddressgroup.addresstype.DefaultMacAddress; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.targetaddressgroup.addresstype.DefaultMepId; @@ -97,8 +94,6 @@ import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitloopback. import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitloopback.transmitloopbackinput.TargetAddress; import org.onosproject.yang.gen.v1.mseasoamfm.rev20160229.mseasoamfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.AugmentedMseaCfmMaintenanceAssociationEndPoint; import org.onosproject.yang.gen.v1.mseasoamfm.rev20160229.mseasoamfm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.DefaultAugmentedMseaCfmMaintenanceAssociationEndPoint; -import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.Identifier45; -import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MacAddressAndUintStr; import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MdLevelType; import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MepIdType; import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.PriorityType; @@ -128,46 +123,34 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour public boolean createMep(MdId mdName, MaIdShort maName, Mep mep) throws CfmConfigException { NetconfController controller = checkNotNull(handler().get(NetconfController.class)); - NetconfSession session = controller.getDevicesMap() - .get(handler().data().deviceId()).getSession(); + NetconfSession session = checkNotNull(controller.getDevicesMap() + .get(handler().data().deviceId()).getSession()); MseaCfmNetconfService mseaCfmService = checkNotNull(handler().get(MseaCfmNetconfService.class)); + CfmMepService cfmMepService = + checkNotNull(handler().get(CfmMepService.class)); MaintenanceAssociationEndPoint yangMep = buildYangMepFromApiMep(mep); CfmMdService cfmMdService = checkNotNull(handler().get(CfmMdService.class)); - MaintenanceDomain md = cfmMdService.getMaintenanceDomain(mdName).get(); - MaintenanceAssociation ma = cfmMdService.getMaintenanceAssociation(mdName, maName).get(); + MseaCfmOpParam mseaCfmOpParam = getMaYangObject(cfmMdService, mdName, maName); - if (!ma.remoteMepIdList().contains(mep.mepId())) { - throw new CfmConfigException("Mep Id " + mep.mepId() + - " is not present in the remote Mep list for MA " + ma.maId() + - ". This is required for EA1000."); - } else if (md.mdNumericId() <= 0 || md.mdNumericId() > NUMERIC_ID_MAX) { - throw new CfmConfigException("Numeric id of MD " + mdName + " must" - + " be between 1 and 64 inclusive for EA1000"); - } else if (ma.maNumericId() <= 0 || ma.maNumericId() > NUMERIC_ID_MAX) { - throw new CfmConfigException("Numeric id of MA " + maName + " must" - + " be between 1 and 64 inclusive for EA1000"); - } + mseaCfmOpParam.mefCfm().maintenanceDomain().get(0) + .maintenanceAssociation().get(0).addToMaintenanceAssociationEndPoint(yangMep); + //Add this mepId to the list of remoteMeps on the device + mseaCfmOpParam.mefCfm().maintenanceDomain().get(0) + .maintenanceAssociation().get(0).addToRemoteMeps(MepIdType.of(mep.mepId().value())); - org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain - .MaintenanceAssociation yangMa = buildYangMaFromApiMa(ma); - yangMa.addToMaintenanceAssociationEndPoint(yangMep); - - org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm - .mefcfm.MaintenanceDomain yangMd = buildYangMdFromApiMd(md); - yangMd.addToMaintenanceAssociation(yangMa); - - MefCfm mefCfm = new DefaultMefCfm(); - mefCfm.addToMaintenanceDomain(yangMd); - - MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam(); - mseaCfmOpParam.mefCfm(mefCfm); + //Add all of the existing meps on this MD/MA to the remote meps list + cfmMepService.getAllMeps(mdName, maName).forEach(m -> { + mseaCfmOpParam.mefCfm().maintenanceDomain().get(0) + .maintenanceAssociation().get(0).addToRemoteMeps(MepIdType.of(m.mepId().value())); + }); try { mseaCfmService.setMseaCfm(mseaCfmOpParam, session, DatastoreId.RUNNING); log.info("Created MEP {} on device {}", mdName + "/" + maName + "/" + mep.mepId(), handler().data().deviceId()); + return true; } catch (NetconfException e) { log.error("Unable to create MEP {}/{}/{} on device {}", @@ -176,11 +159,6 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour } } - @Override - public Collection getAllMeps(MdId mdName, MaIdShort maName) throws CfmConfigException { - throw new UnsupportedOperationException("Not yet implemented"); - } - @Override public MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException { @@ -189,31 +167,20 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour throw new CfmConfigException("Device is not ready - connecting or " + "disconnected for MEP " + mdName + "/" + maName + "/" + mepId); } - NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession(); + NetconfSession session = checkNotNull(controller.getDevicesMap() + .get(handler().data().deviceId()).getSession()); MseaCfmNetconfService mseaCfmService = checkNotNull(handler().get(MseaCfmNetconfService.class)); try { MseaCfm mseacfm = mseaCfmService.getMepFull(mdName, maName, mepId, session); - if (mseacfm != null && mseacfm.mefCfm() != null && - mseacfm.mefCfm().maintenanceDomain() != null) { - for (org.onosproject.yang.gen.v1.mseacfm.rev20160229. - mseacfm.mefcfm.MaintenanceDomain replyMd : - mseacfm.mefCfm().maintenanceDomain()) { - for (org.onosproject.yang.gen.v1.mseacfm.rev20160229. - mseacfm.mefcfm.maintenancedomain. - MaintenanceAssociation replyMa : - replyMd.maintenanceAssociation()) { - for (MaintenanceAssociationEndPoint replyMep : - replyMa.maintenanceAssociationEndPoint()) { - return buildApiMepEntryFromYangMep( - replyMep, handler().data().deviceId(), mdName, maName); - } - } - } + Collection mepEntries = getMepEntriesFromYangResponse(mseacfm); + if (mepEntries == null || mepEntries.size() != 1) { + log.warn("Mep " + mepId + " not found on device " + handler().data().deviceId()); + return null; + } else { + return mepEntries.stream().findFirst().get(); } - log.warn("Mep " + mepId + " not found on device " + handler().data().deviceId()); - return null; } catch (NetconfException e) { log.error("Unable to get MEP {}/{}/{} on device {}", mdName, maName, mepId, handler().data().deviceId()); @@ -221,12 +188,37 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour } } + private Collection getMepEntriesFromYangResponse(MseaCfm mseacfm) + throws CfmConfigException { + + Collection mepEntries = new ArrayList<>(); + if (mseacfm == null || mseacfm.mefCfm() == null || mseacfm.mefCfm().maintenanceDomain() == null) { + return mepEntries; + } + + for (org.onosproject.yang.gen.v1.mseacfm.rev20160229. + mseacfm.mefcfm.MaintenanceDomain replyMd:mseacfm.mefCfm().maintenanceDomain()) { + for (org.onosproject.yang.gen.v1.mseacfm.rev20160229. + mseacfm.mefcfm.maintenancedomain. + MaintenanceAssociation replyMa:replyMd.maintenanceAssociation()) { + for (MaintenanceAssociationEndPoint replyMep:replyMa.maintenanceAssociationEndPoint()) { + mepEntries.add(buildApiMepEntryFromYangMep( + replyMep, handler().data().deviceId(), replyMd, replyMa)); + } + } + } + return mepEntries; + } + @Override - public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException { + public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId, + Optional oldMd) throws CfmConfigException { NetconfController controller = checkNotNull(handler().get(NetconfController.class)); - NetconfSession session = controller.getDevicesMap().get(handler().data().deviceId()).getSession(); + NetconfSession session = checkNotNull(controller.getDevicesMap() + .get(handler().data().deviceId()).getSession()); MseaCfmNetconfService mseaCfmService = checkNotNull(handler().get(MseaCfmNetconfService.class)); + CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class)); MaintenanceAssociationEndPoint mep = new DefaultMaintenanceAssociationEndPoint(); @@ -234,12 +226,34 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain .MaintenanceAssociation yangMa = new DefaultMaintenanceAssociation(); - yangMa.maNameAndTypeCombo(MaNameUtil.getYangMaNameFromApiMaId(maName)); + Short maNumericId = null; + try { + maNumericId = + mdService.getMaintenanceAssociation(mdName, maName).get().maNumericId(); + yangMa.id(maNumericId); + } catch (NoSuchElementException | IllegalArgumentException e) { + //The MA and/or MD have probably been deleted + // try to get numeric id values from oldMd + log.debug("Could not get MD/MA details from MD service during deletion of MEP {}." + + "Continuing with values from event", new MepKeyId(mdName, maName, mepId)); + yangMa.id(getMaNumericId(oldMd.get(), maName)); + } + yangMa.addToMaintenanceAssociationEndPoint(mep); org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain yangMd = new DefaultMaintenanceDomain(); - yangMd.mdNameAndTypeCombo(getYangMdNameFromApiMdId(mdName)); + Short mdNumericId = null; + try { + mdNumericId = mdService.getMaintenanceDomain(mdName).get().mdNumericId(); + yangMd.id(mdNumericId); + } catch (NoSuchElementException | IllegalArgumentException e) { + //The MD has probably been deleted + // try to get numeric id values from oldMd + log.debug("Could not get MD details from MD service during deletion of MEP {}." + + "Continuing with values from event", new MepKeyId(mdName, maName, mepId)); + yangMd.id(oldMd.get().mdNumericId()); + } yangMd.addToMaintenanceAssociation(yangMa); MefCfm mefCfm = new DefaultMefCfm(); @@ -254,13 +268,280 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour "/" + mepId, handler().data().deviceId()); return true; } catch (NetconfException e) { - log.error("Unable to delete MEP {}/{}/{} on device {}", - mdName, maName, mepId, handler().data().deviceId()); + log.error("Unable to delete MEP {} ({}) on device {}", + mdName + "/" + maName + "/" + mepId, + mdNumericId + "/" + maNumericId, handler().data().deviceId(), e); throw new CfmConfigException("Unable to delete MEP :" + e.getMessage()); } } + @Override + public boolean createMdOnDevice(MdId mdId) throws CfmConfigException { + NetconfController controller = + checkNotNull(handler().get(NetconfController.class)); + NetconfSession session = checkNotNull(controller.getDevicesMap() + .get(handler().data().deviceId()).getSession()); + + CfmMdService cfmMdService = checkNotNull(handler().get(CfmMdService.class)); + MaintenanceDomain md = cfmMdService.getMaintenanceDomain(mdId).get(); + + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm + .mefcfm.MaintenanceDomain yangMd = buildYangMdFromApiMd(md); + + if (md.mdNumericId() <= 0 || md.mdNumericId() > NUMERIC_ID_MAX) { + throw new CfmConfigException("Numeric id of MD " + mdId + " must" + + " be between 1 and 64 inclusive for EA1000"); + } + + for (MaintenanceAssociation ma:md.maintenanceAssociationList()) { + if (ma.maNumericId() <= 0 || ma.maNumericId() > NUMERIC_ID_MAX) { + throw new CfmConfigException("Numeric id of MA " + mdId + " must" + + " be between 1 and 64 inclusive for EA1000"); + } + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain + .MaintenanceAssociation yangMa = buildYangMaFromApiMa(ma); + yangMd.addToMaintenanceAssociation(yangMa); + } + + MefCfm mefCfm = new DefaultMefCfm(); + mefCfm.addToMaintenanceDomain(yangMd); + + MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam(); + mseaCfmOpParam.mefCfm(mefCfm); + + MseaCfmNetconfService mseaCfmService = + checkNotNull(handler().get(MseaCfmNetconfService.class)); + + try { + boolean created = mseaCfmService.setMseaCfm(mseaCfmOpParam, session, DatastoreId.RUNNING); + log.info("Created MD {} on device {}", mdId.mdName(), + handler().data().deviceId()); + return created; + } catch (NetconfException e) { + log.error("Unable to create MD {} on device {}", + mdId.mdName(), handler().data().deviceId()); + throw new CfmConfigException("Unable to create MD :" + e.getMessage()); + } + } + + @Override + public boolean createMaOnDevice(MdId mdId, MaIdShort maId) throws CfmConfigException { + NetconfController controller = + checkNotNull(handler().get(NetconfController.class)); + NetconfSession session = checkNotNull(controller.getDevicesMap() + .get(handler().data().deviceId()).getSession()); + + CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class)); + MseaCfmOpParam mseaCfmOpParam = getMaYangObject(mdService, mdId, maId); + MseaCfmNetconfService mseaCfmService = + checkNotNull(handler().get(MseaCfmNetconfService.class)); + + try { + boolean created = mseaCfmService.setMseaCfm(mseaCfmOpParam, session, DatastoreId.RUNNING); + log.info("Created MA {} on device {}", mdId.mdName() + "/" + maId.maName(), + handler().data().deviceId()); + return created; + } catch (NetconfException e) { + log.error("Unable to create MA {} on device {}", + mdId.mdName() + "/" + maId.maName(), handler().data().deviceId()); + throw new CfmConfigException("Unable to create MA :" + e.getMessage()); + } + } + + private static MseaCfmOpParam getMaYangObject(CfmMdService cfmMdService, + MdId mdName, MaIdShort maName) throws CfmConfigException { + MaintenanceDomain md = cfmMdService.getMaintenanceDomain(mdName).get(); + MaintenanceAssociation ma = cfmMdService.getMaintenanceAssociation(mdName, maName).get(); + + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain + .MaintenanceAssociation yangMa = buildYangMaFromApiMa(ma); + + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm + .mefcfm.MaintenanceDomain yangMd = buildYangMdFromApiMd(md); + yangMd.addToMaintenanceAssociation(yangMa); + + if (md.mdNumericId() <= 0 || md.mdNumericId() > NUMERIC_ID_MAX) { + throw new CfmConfigException("Numeric id of MD " + mdName + " must" + + " be between 1 and 64 inclusive for EA1000"); + } else if (ma.maNumericId() <= 0 || ma.maNumericId() > NUMERIC_ID_MAX) { + throw new CfmConfigException("Numeric id of MA " + maName + " must" + + " be between 1 and 64 inclusive for EA1000"); + } + + MefCfm mefCfm = new DefaultMefCfm(); + mefCfm.addToMaintenanceDomain(yangMd); + + MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam(); + mseaCfmOpParam.mefCfm(mefCfm); + + return mseaCfmOpParam; + } + + @Override + public boolean deleteMdOnDevice(MdId mdId, Optional oldMd) + throws CfmConfigException { + NetconfController controller = + checkNotNull(handler().get(NetconfController.class)); + NetconfSession session = controller.getDevicesMap() + .get(handler().data().deviceId()).getSession(); + + //First check if this MD is known to ONOS if it is does it have MAs and + // do they have any Meps known to ONOS. If there are Meps throw an exception - + // the Meps should have been deleted first + //If there are none known to ONOS we do not check for Meps on the actual device + // - there might might be some orphaned ones down there - we want to delete these + //FIXME: When CfmMepService is extended to be persistent come back and enable check + CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class)); + MseaCfmNetconfService mseaCfmService = + checkNotNull(handler().get(MseaCfmNetconfService.class)); + + MdNameAndTypeCombo mdName = getYangMdNameFromApiMdId(mdId); + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain yangMd = + new DefaultMaintenanceDomain(); + Short mdNumericId = null; + try { + mdNumericId = mdService.getMaintenanceDomain(mdId).get().mdNumericId(); + yangMd.id(mdNumericId); + } catch (NoSuchElementException e) { + yangMd.id(oldMd.get().mdNumericId()); + } + + MefCfm mefCfm = new DefaultMefCfm(); + mefCfm.addToMaintenanceDomain(yangMd); + + MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam(); + mseaCfmOpParam.mefCfm(mefCfm); + + try { + boolean deleted = mseaCfmService.deleteMseaMd(mseaCfmOpParam, session, DatastoreId.RUNNING); + log.info("Deleted MD {} on device {}", mdName, + handler().data().deviceId()); + return deleted; + } catch (NetconfException e) { + log.error("Unable to delete MD {} ({}) on device {}", + mdName, mdNumericId, handler().data().deviceId()); + throw new CfmConfigException("Unable to delete MD :" + e.getMessage()); + } + + } + + @Override + public boolean deleteMaOnDevice(MdId mdId, MaIdShort maId, Optional oldMd) + throws CfmConfigException { + NetconfController controller = + checkNotNull(handler().get(NetconfController.class)); + NetconfSession session = controller.getDevicesMap() + .get(handler().data().deviceId()).getSession(); + + CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class)); + + MseaCfmNetconfService mseaCfmService = + checkNotNull(handler().get(MseaCfmNetconfService.class)); + + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain + .MaintenanceAssociation yangMa = new DefaultMaintenanceAssociation(); + Short maNumericId = null; + try { + maNumericId = + mdService.getMaintenanceAssociation(mdId, maId).get().maNumericId(); + yangMa.id(maNumericId); + } catch (NoSuchElementException e) { + yangMa.id(getMaNumericId(oldMd.get(), maId)); + } + + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain yangMd = + new DefaultMaintenanceDomain(); + Short mdNumericId = null; + try { + mdNumericId = mdService.getMaintenanceDomain(mdId).get().mdNumericId(); + yangMd.id(mdNumericId); + } catch (NoSuchElementException e) { + yangMd.id(oldMd.get().mdNumericId()); + } + yangMd.addToMaintenanceAssociation(yangMa); + + MefCfm mefCfm = new DefaultMefCfm(); + mefCfm.addToMaintenanceDomain(yangMd); + + MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam(); + mseaCfmOpParam.mefCfm(mefCfm); + + try { + boolean deleted = mseaCfmService.deleteMseaMa(mseaCfmOpParam, session, DatastoreId.RUNNING); + log.info("Deleted MA {} ({})on device {}", mdId.mdName() + "/" + maId.maName(), + mdNumericId + "/" + maNumericId, handler().data().deviceId()); + return deleted; + } catch (NetconfException e) { + log.error("Unable to delete MA {} ({}) on device {}", + mdId.mdName() + "/" + maId.maName(), + mdNumericId + "/" + maNumericId, handler().data().deviceId()); + throw new CfmConfigException("Unable to delete MA :" + e.getMessage()); + } + } + + @Override + public boolean createMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException { + return crDelMaRemoteMep(mdId, maId, remoteMep, true); + } + + @Override + public boolean deleteMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException { + return crDelMaRemoteMep(mdId, maId, remoteMep, false); + } + + private boolean crDelMaRemoteMep(MdId mdId, MaIdShort maId, MepId remoteMep, + boolean isCreate) throws CfmConfigException { + NetconfController controller = + checkNotNull(handler().get(NetconfController.class)); + NetconfSession session = controller.getDevicesMap() + .get(handler().data().deviceId()).getSession(); + + CfmMdService mdService = checkNotNull(handler().get(CfmMdService.class)); + + Short mdNumericId = mdService.getMaintenanceDomain(mdId).get().mdNumericId(); + Short maNumericId = + mdService.getMaintenanceAssociation(mdId, maId).get().maNumericId(); + + MseaCfmNetconfService mseaCfmService = + checkNotNull(handler().get(MseaCfmNetconfService.class)); + + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain + .MaintenanceAssociation yangMa = new DefaultMaintenanceAssociation(); + yangMa.id(maNumericId); + yangMa.addToRemoteMeps(MepIdType.of(remoteMep.value())); + + org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.MaintenanceDomain yangMd = + new DefaultMaintenanceDomain(); + yangMd.id(mdNumericId); + yangMd.addToMaintenanceAssociation(yangMa); + + MefCfm mefCfm = new DefaultMefCfm(); + mefCfm.addToMaintenanceDomain(yangMd); + + MseaCfmOpParam mseaCfmOpParam = new MseaCfmOpParam(); + mseaCfmOpParam.mefCfm(mefCfm); + + try { + boolean result = false; + if (isCreate) { + result = mseaCfmService.setMseaCfm(mseaCfmOpParam, session, DatastoreId.RUNNING); + } else { + result = mseaCfmService.deleteMseaMaRMep(mseaCfmOpParam, session, DatastoreId.RUNNING); + } + log.info("{} Remote MEP {} in MA {} on device {}", isCreate ? "Created" : "Deleted", + remoteMep, mdId.mdName() + "/" + maId.maName(), handler().data().deviceId()); + return result; + } catch (NetconfException e) { + log.error("Unable to {} RemoteMep {} in MA {} on device {}", + isCreate ? "create" : "delete", remoteMep, mdId.mdName() + "/" + maId.maName(), + handler().data().deviceId()); + throw new CfmConfigException("Unable to " + (isCreate ? "create" : "delete") + + " Remote Mep:" + e.getMessage()); + } + } + + @Override public void transmitLoopback(MdId mdName, MaIdShort maName, MepId mepId, MepLbCreate lbCreate) throws CfmConfigException { @@ -361,7 +642,7 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour throw new UnsupportedOperationException("Not yet implemented"); } - private org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm + private static org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm .MaintenanceDomain buildYangMdFromApiMd(MaintenanceDomain md) throws CfmConfigException { MdNameAndTypeCombo mdName = getYangMdNameFromApiMdId(md.mdId()); @@ -375,44 +656,7 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour return mdYang; } - protected static MdNameAndTypeCombo getYangMdNameFromApiMdId(MdId mdId) - throws CfmConfigException { - MdNameAndTypeCombo mdName; - if (mdId instanceof MdIdDomainName) { - boolean isIpAddr = false; - try { - if (IpAddress.valueOf(mdId.mdName()) != null) { - isIpAddr = true; - } - } catch (IllegalArgumentException e) { - //continue - } - if (isIpAddr) { - mdName = new DefaultNameDomainName(); - ((DefaultNameDomainName) mdName).nameDomainName(NameDomainNameUnion.of( - org.onosproject.yang.gen.v1.ietfinettypes.rev20130715.ietfinettypes. - IpAddress.fromString(mdId.mdName()))); - } else { - mdName = new DefaultNameDomainName(); - ((DefaultNameDomainName) mdName).nameDomainName(NameDomainNameUnion - .of(DomainName.fromString(mdId.mdName()))); - } - } else if (mdId instanceof MdIdMacUint) { - mdName = new DefaultMacAddressAndUint(); - ((DefaultMacAddressAndUint) mdName).nameMacAddressAndUint(MacAddressAndUintStr.fromString(mdId.mdName())); - } else if (mdId instanceof MdIdNone) { - mdName = new DefaultNameNone(); - } else if (mdId instanceof MdIdCharStr) { - mdName = new DefaultNameCharacterString(); - ((DefaultNameCharacterString) mdName).name(Identifier45.fromString(mdId.mdName())); - } else { - throw new CfmConfigException("Unexpected error creating MD " + - mdId.getClass().getSimpleName()); - } - return mdName; - } - - private org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm + private static org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm .maintenancedomain.MaintenanceAssociation buildYangMaFromApiMa( MaintenanceAssociation apiMa) throws CfmConfigException { @@ -422,12 +666,6 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour .MaintenanceAssociation yamgMa = new DefaultMaintenanceAssociation(); yamgMa.maNameAndTypeCombo(maName); - if (apiMa.remoteMepIdList() == null || apiMa.remoteMepIdList().size() < REMOTEMEPLIST_MIN_COUNT - || apiMa.remoteMepIdList().size() > REMOTEMEPLIST_MAX_COUNT) { - throw new CfmConfigException("EA1000 requires between " + - REMOTEMEPLIST_MIN_COUNT + " and " + REMOTEMEPLIST_MAX_COUNT + - " remote meps in an MA"); - } for (MepId rmep:apiMa.remoteMepIdList()) { yamgMa.addToRemoteMeps(MepIdType.of(rmep.id())); } @@ -486,7 +724,7 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour return yamgMa; } - private MaintenanceAssociationEndPoint buildYangMepFromApiMep(Mep mep) + private static MaintenanceAssociationEndPoint buildYangMepFromApiMep(Mep mep) throws CfmConfigException { MaintenanceAssociationEndPoint mepBuilder = new DefaultMaintenanceAssociationEndPoint(); @@ -515,14 +753,19 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour private MepEntry buildApiMepEntryFromYangMep( MaintenanceAssociationEndPoint yangMep, DeviceId deviceId, - MdId mdName, MaIdShort maName) throws CfmConfigException { + org.onosproject.yang.gen.v1.mseacfm.rev20160229. + mseacfm.mefcfm.MaintenanceDomain replyMd, + org.onosproject.yang.gen.v1.mseacfm.rev20160229. + mseacfm.mefcfm.maintenancedomain.MaintenanceAssociation replyMa) + throws CfmConfigException { MepId mepId = MepId.valueOf((short) yangMep.mepIdentifier().uint16()); MepEntry.MepEntryBuilder builder = DefaultMepEntry.builder(mepId, deviceId, (yangMep.yangAutoPrefixInterface() == InterfaceEnum.ETH0) ? PortNumber.portNumber(0) : PortNumber.portNumber(1), MepDirection.DOWN_MEP, //Always down for EA1000 - mdName, maName); + getApiMdIdFromYangMdName(replyMd.mdNameAndTypeCombo()), + getApiMaIdFromYangMaName(replyMa.maNameAndTypeCombo())); if (yangMep.loopback() != null) { MepLbEntryBuilder lbEntryBuilder = DefaultMepLbEntry.builder(); @@ -614,4 +857,10 @@ public class EA1000CfmMepProgrammable extends AbstractHandlerBehaviour } return rmepBuilder.build(); } + + private static short getMaNumericId(MaintenanceDomain md, MaIdShort maId) { + return md.maintenanceAssociationList().stream() + .filter(ma -> maId.equals(ma.maId())) + .findFirst().get().maNumericId(); + } } diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/MseaCfmNetconfService.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/MseaCfmNetconfService.java index 1bc32ca7ac..cbe41ba270 100644 --- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/MseaCfmNetconfService.java +++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/MseaCfmNetconfService.java @@ -18,6 +18,7 @@ package org.onosproject.drivers.microsemi.yang; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; import org.onosproject.incubator.net.l2monitoring.soam.SoamId; import org.onosproject.netconf.DatastoreId; import org.onosproject.netconf.NetconfException; @@ -29,6 +30,8 @@ import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitlinktrace import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitlinktrace.TransmitLinktraceOutput; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitloopback.TransmitLoopbackInput; +import java.util.Optional; + /** * Extension of mseaCfmService to include NETCONF sessions. * @@ -65,6 +68,19 @@ public interface MseaCfmNetconfService { MseaCfm getMepFull(MdId mdId, MaIdShort maId, MepId mepId, NetconfSession session) throws NetconfException; + + /** + * Returns set of all MepIds from one Md or Ma or all. + * + * @param mdIdOptional An MdId to filter by, or empty to select all + * @param maIdOptional An MaId to filter by, or empty to select all + * @param session An active NETCONF session + * @param targetDs one of running, candidate or startup + * @return mseaCfm + * @throws NetconfException if the session has any error + */ + MseaCfm getMepIds(Optional mdIdOptional, Optional maIdOptional, + NetconfSession session, DatastoreId targetDs) throws NetconfException; /** * Returns attributes of DM. * @@ -102,10 +118,52 @@ public interface MseaCfmNetconfService { * @param targetDs one of running, candidate or startup * @return Boolean to indicate success or failure * @throws NetconfException if the session has any error + * @throws CfmConfigException if the Cfm config has any error */ boolean deleteMseaMep(MseaCfmOpParam mseaCfm, NetconfSession session, - DatastoreId targetDs) throws NetconfException; + DatastoreId targetDs) throws NetconfException, CfmConfigException; + /** + * Deletes named Ma of mseaCfm. + * Expects to see a list of Mas + * + * @param mseaCfm value of mseaCfm + * @param session An active NETCONF session + * @param targetDs one of running, candidate or startup + * @return Boolean to indicate success or failure + * @throws NetconfException if the session has any error + * @throws CfmConfigException if the Cfm config has any error + */ + boolean deleteMseaMa(MseaCfmOpParam mseaCfm, NetconfSession session, + DatastoreId targetDs) throws NetconfException, CfmConfigException; + + /** + * Deletes a remote Mep from an MA. + * Expects one or more RMeps + * + * @param mseaCfm value of mseaCfm + * @param session An active NETCONF session + * @param targetDs one of running, candidate or startup + * @return Boolean to indicate success or failure + * @throws NetconfException if the session has any error + * @throws CfmConfigException if the Cfm config has any error + */ + boolean deleteMseaMaRMep(MseaCfmOpParam mseaCfm, NetconfSession session, + DatastoreId targetDs) throws NetconfException, CfmConfigException; + + /** + * Deletes named Md of mseaCfm. + * Expects to see a list of Mds + * + * @param mseaCfm value of mseaCfm + * @param session An active NETCONF session + * @param targetDs one of running, candidate or startup + * @return Boolean to indicate success or failure + * @throws NetconfException if the session has any error + * @throws CfmConfigException if the Cfm config has any error + */ + boolean deleteMseaMd(MseaCfmOpParam mseaCfm, NetconfSession session, + DatastoreId targetDs) throws NetconfException, CfmConfigException; /** * Deletes named delay measurements of mseaCfm. @@ -116,9 +174,10 @@ public interface MseaCfmNetconfService { * @param targetDs one of running, candidate or startup * @return Boolean to indicate success or failure * @throws NetconfException if the session has any error + * @throws CfmConfigException if the Cfm config has any error */ boolean deleteMseaCfmDm(MseaCfmOpParam mseaCfm, NetconfSession session, - DatastoreId targetDs) throws NetconfException; + DatastoreId targetDs) throws NetconfException, CfmConfigException; /** * Service interface of transmitLoopback. diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaCfmManager.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaCfmManager.java index c39625bcdc..692e9a848d 100644 --- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaCfmManager.java +++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/impl/MseaCfmManager.java @@ -25,6 +25,7 @@ import org.onosproject.drivers.microsemi.yang.MseaCfmNetconfService; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; import org.onosproject.incubator.net.l2monitoring.soam.SoamId; import org.onosproject.netconf.DatastoreId; import org.onosproject.netconf.NetconfException; @@ -42,6 +43,7 @@ import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.transmitloopback. import org.onosproject.yang.gen.v1.mseasoampm.rev20160229.mseasoampm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.AugmentedMseaCfmMaintenanceAssociationEndPoint; import org.onosproject.yang.gen.v1.mseasoampm.rev20160229.mseasoampm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.DefaultAugmentedMseaCfmMaintenanceAssociationEndPoint; import org.onosproject.yang.gen.v1.mseasoampm.rev20160229.mseasoampm.mefcfm.maintenancedomain.maintenanceassociation.maintenanceassociationendpoint.augmentedmseacfmmaintenanceassociationendpoint.delaymeasurements.DelayMeasurement; +import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MepIdType; import org.onosproject.yang.model.DefaultModelObjectData; import org.onosproject.yang.model.ModelConverter; import org.onosproject.yang.model.ModelObject; @@ -54,6 +56,7 @@ import org.onosproject.yang.runtime.DefaultAnnotation; import org.onosproject.yang.runtime.DefaultCompositeStream; import java.io.ByteArrayInputStream; +import java.util.Optional; import java.util.regex.Pattern; /** @@ -77,6 +80,19 @@ public class MseaCfmManager extends AbstractYangServiceImpl @Deprecated protected static final Pattern REGEX_EMPTY_LAST_DEFECT_SENT = Pattern.compile("()", Pattern.DOTALL); + public static final String MEF_CFM = "mef-cfm"; + public static final String MAINTENANCE_DOMAIN = "maintenance-domain"; + public static final String ID = "id"; + public static final String MAINTENANCE_ASSOCIATION = "maintenance-association"; + public static final String TRANSMIT_LOOPBACK = "transmit-loopback"; + public static final String ABORT_LOOPBACK = "abort-loopback"; + public static final String MAINTENANCE_ASSOCIATION_END_POINT = "maintenance-association-end-point"; + public static final String MEP_ID = "mep-id"; + public static final String DELAY_MEASUREMENTS = "delay-measurements"; + public static final String DELAY_MEASUREMENT = "delay-measurement"; + public static final String DM_ID = "dm-id"; + public static final String MEP_IDENTIFIER = "mep-identifier"; + public static final String REMOTE_MEPS = "remote-meps"; @Activate public void activate() { @@ -152,6 +168,38 @@ public class MseaCfmManager extends AbstractYangServiceImpl return mseaCfm; } + @Override + public MseaCfm getMepIds(Optional mdIdOptional, Optional maIdOptional, + NetconfSession session, DatastoreId targetDs) throws NetconfException { + + ModelObjectData.Builder moQueryBldr = DefaultModelObjectData.builder(); + + ArrayList annotations = new ArrayList(); + String xmlQueryStr = encodeMoToXmlStr(moQueryBldr.build(), annotations); + + log.debug("Sending for full MEP" + + " query on NETCONF session " + session.getSessionId() + + ":\n" + xmlQueryStr); + + String xmlResult = session.get(xmlQueryStr, null); + xmlResult = removeRpcReplyData(xmlResult); + xmlResult = removeEmptyActiveDefects(xmlResult); + DefaultCompositeStream resultDcs = new DefaultCompositeStream( + null, new ByteArrayInputStream(xmlResult.getBytes())); + CompositeData compositeData = xSer.decode(resultDcs, yCtx); + + ModelObjectData mod = ((ModelConverter) yangModelRegistry).createModel(compositeData.resourceData()); + + MseaCfmOpParam mseaCfm = new MseaCfmOpParam(); + for (ModelObject mo:mod.modelObjects()) { + if (mo instanceof DefaultMefCfm) { + mseaCfm.mefCfm((DefaultMefCfm) mo); + } + } + + return mseaCfm; + } + @Override public MseaCfm getSoamDm(MdId mdName, MaIdShort maName, MepId mepId, SoamId dmId, DmEntryParts parts, NetconfSession session) @@ -190,7 +238,7 @@ public class MseaCfmManager extends AbstractYangServiceImpl @Override public boolean deleteMseaCfmDm(MseaCfmOpParam mseaCfm, NetconfSession session, - DatastoreId targetDs) throws NetconfException { + DatastoreId targetDs) throws NetconfException, CfmConfigException { ModelObjectData mseCfmDmList = DefaultModelObjectData.builder() .addModelObject((ModelObject) mseaCfm).build(); @@ -198,7 +246,13 @@ public class MseaCfmManager extends AbstractYangServiceImpl ArrayList anis = new ArrayList(); if (mseaCfm != null && mseaCfm.mefCfm() != null) { for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) { + if (md.id() == 0) { + throw new CfmConfigException("An MD numeric ID must be given"); + } for (MaintenanceAssociation ma:md.maintenanceAssociation()) { + if (ma.id() == 0) { + throw new CfmConfigException("An MA numeric ID must be given"); + } for (MaintenanceAssociationEndPoint mep:ma.maintenanceAssociationEndPoint()) { AugmentedMseaCfmMaintenanceAssociationEndPoint mepAugment = mep.augmentation(DefaultAugmentedMseaCfmMaintenanceAssociationEndPoint.class); @@ -206,16 +260,16 @@ public class MseaCfmManager extends AbstractYangServiceImpl for (DelayMeasurement dms:mepAugment.delayMeasurements().delayMeasurement()) { ResourceId.Builder ridBuilder = ResourceId.builder() .addBranchPointSchema("/", null) - .addBranchPointSchema("mef-cfm", MSEA_CFM_NS) - .addBranchPointSchema("maintenance-domain", MSEA_CFM_NS) - .addKeyLeaf("id", MSEA_CFM_NS, md.id()) - .addBranchPointSchema("maintenance-association", MSEA_CFM_NS) - .addKeyLeaf("id", MSEA_CFM_NS, ma.id()) - .addBranchPointSchema("maintenance-association-end-point", MSEA_CFM_NS) - .addKeyLeaf("mep-id", MSEA_CFM_NS, mep.mepIdentifier()) - .addBranchPointSchema("delay-measurements", MSEA_CFM_PM_NS) - .addBranchPointSchema("delay-measurement", MSEA_CFM_PM_NS) - .addKeyLeaf("dm-id", MSEA_CFM_PM_NS, mep.mepIdentifier()); + .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS) + .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, md.id()) + .addBranchPointSchema(MAINTENANCE_ASSOCIATION, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, ma.id()) + .addBranchPointSchema(MAINTENANCE_ASSOCIATION_END_POINT, MSEA_CFM_NS) + .addKeyLeaf(MEP_ID, MSEA_CFM_NS, mep.mepIdentifier()) + .addBranchPointSchema(DELAY_MEASUREMENTS, MSEA_CFM_PM_NS) + .addBranchPointSchema(DELAY_MEASUREMENT, MSEA_CFM_PM_NS) + .addKeyLeaf(DM_ID, MSEA_CFM_PM_NS, mep.mepIdentifier()); AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder() .resourceId(ridBuilder.build()) .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE)) @@ -233,7 +287,7 @@ public class MseaCfmManager extends AbstractYangServiceImpl @Override public boolean deleteMseaMep(MseaCfmOpParam mseaCfm, NetconfSession session, - DatastoreId targetDs) throws NetconfException { + DatastoreId targetDs) throws NetconfException, CfmConfigException { ModelObjectData mseCfmMepList = DefaultModelObjectData.builder() .addModelObject((ModelObject) mseaCfm.mefCfm()).build(); @@ -241,15 +295,100 @@ public class MseaCfmManager extends AbstractYangServiceImpl ArrayList anis = new ArrayList(); if (mseaCfm.mefCfm() != null) { for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) { + if (md.id() == 0) { + throw new CfmConfigException("An MD numeric ID must be given"); + } for (MaintenanceAssociation ma:md.maintenanceAssociation()) { + if (ma.id() == 0) { + throw new CfmConfigException("An MA numeric ID must be given"); + } for (MaintenanceAssociationEndPoint mep:ma.maintenanceAssociationEndPoint()) { ResourceId.Builder ridBuilder = ResourceId.builder() .addBranchPointSchema("/", null) - .addBranchPointSchema("mef-cfm", MSEA_CFM_NS) - .addBranchPointSchema("maintenance-domain", MSEA_CFM_NS) - .addBranchPointSchema("maintenance-association", MSEA_CFM_NS) - .addBranchPointSchema("maintenance-association-end-point", MSEA_CFM_NS) - .addKeyLeaf("mep-identifier", MSEA_CFM_NS, mep.mepIdentifier().uint16()); + .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS) + .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, md.id()) + .addBranchPointSchema(MAINTENANCE_ASSOCIATION, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, ma.id()) + .addBranchPointSchema(MAINTENANCE_ASSOCIATION_END_POINT, MSEA_CFM_NS) + .addKeyLeaf(MEP_IDENTIFIER, MSEA_CFM_NS, mep.mepIdentifier().uint16()); + AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder() + .resourceId(ridBuilder.build()) + .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE)) + .build(); + anis.add(ani); + } + } + } + } + + return setNetconfObject(mseCfmMepList, session, targetDs, anis); + } + + @Override + public boolean deleteMseaMa(MseaCfmOpParam mseaCfm, NetconfSession session, + DatastoreId targetDs) throws NetconfException, CfmConfigException { + + ModelObjectData mseCfmMepList = DefaultModelObjectData.builder() + .addModelObject((ModelObject) mseaCfm.mefCfm()).build(); + + ArrayList anis = new ArrayList(); + if (mseaCfm != null && mseaCfm.mefCfm() != null) { + for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) { + if (md.id() == 0) { + throw new CfmConfigException("An MD numeric ID must be given"); + } + for (MaintenanceAssociation ma:md.maintenanceAssociation()) { + if (ma.id() == 0) { + throw new CfmConfigException("An MA numeric ID must be given"); + } + ResourceId.Builder ridBuilder = ResourceId.builder() + .addBranchPointSchema("/", null) + .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS) + .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, md.id()) + .addBranchPointSchema(MAINTENANCE_ASSOCIATION, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, ma.id()); + + AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder() + .resourceId(ridBuilder.build()) + .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE)) + .build(); + anis.add(ani); + } + } + } + + return setNetconfObject(mseCfmMepList, session, targetDs, anis); + } + + @Override + public boolean deleteMseaMaRMep(MseaCfmOpParam mseaCfm, NetconfSession session, + DatastoreId targetDs) throws NetconfException, CfmConfigException { + + ModelObjectData mseCfmMepList = DefaultModelObjectData.builder() + .addModelObject((ModelObject) mseaCfm.mefCfm()).build(); + + ArrayList anis = new ArrayList(); + if (mseaCfm != null && mseaCfm.mefCfm() != null) { + for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) { + if (md.id() == 0) { + throw new CfmConfigException("An MD numeric ID must be given"); + } + for (MaintenanceAssociation ma:md.maintenanceAssociation()) { + if (ma.id() == 0) { + throw new CfmConfigException("An MA numeric ID must be given"); + } + for (MepIdType rmep:ma.remoteMeps()) { + ResourceId.Builder ridBuilder = ResourceId.builder() + .addBranchPointSchema("/", null) + .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS) + .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, md.id()) + .addBranchPointSchema(MAINTENANCE_ASSOCIATION, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, ma.id()) + .addLeafListBranchPoint(REMOTE_MEPS, MSEA_CFM_NS, + Integer.valueOf(rmep.uint16())); AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder() .resourceId(ridBuilder.build()) .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE)) @@ -264,6 +403,35 @@ public class MseaCfmManager extends AbstractYangServiceImpl } + @Override + public boolean deleteMseaMd(MseaCfmOpParam mseaCfm, NetconfSession session, + DatastoreId targetDs) throws NetconfException, CfmConfigException { + + ModelObjectData mseCfmMepList = DefaultModelObjectData.builder() + .addModelObject((ModelObject) mseaCfm.mefCfm()).build(); + + ArrayList anis = new ArrayList(); + if (mseaCfm != null && mseaCfm.mefCfm() != null) { + for (MaintenanceDomain md:mseaCfm.mefCfm().maintenanceDomain()) { + if (md.id() == 0) { + throw new CfmConfigException("An MD numeric ID must be given"); + } + ResourceId.Builder ridBuilder = ResourceId.builder() + .addBranchPointSchema("/", null) + .addBranchPointSchema(MEF_CFM, MSEA_CFM_NS) + .addBranchPointSchema(MAINTENANCE_DOMAIN, MSEA_CFM_NS) + .addKeyLeaf(ID, MSEA_CFM_NS, md.id()); + AnnotatedNodeInfo ani = DefaultAnnotatedNodeInfo.builder() + .resourceId(ridBuilder.build()) + .addAnnotation(new DefaultAnnotation(NC_OPERATION, OP_DELETE)) + .build(); + anis.add(ani); + } + } + + return setNetconfObject(mseCfmMepList, session, targetDs, anis); + } + /** * Call RPCs on the device through NETCONF. */ @@ -275,7 +443,7 @@ public class MseaCfmManager extends AbstractYangServiceImpl .addModelObject((ModelObject) inputVar).build(); customRpcNetconf(transLoopbackMo, - "transmit-loopback", session); + TRANSMIT_LOOPBACK, session); } @Override @@ -284,7 +452,7 @@ public class MseaCfmManager extends AbstractYangServiceImpl ModelObjectData abortLoopbackMo = DefaultModelObjectData.builder() .addModelObject((ModelObject) inputVar).build(); - customRpcNetconf(abortLoopbackMo, "abort-loopback", session); + customRpcNetconf(abortLoopbackMo, ABORT_LOOPBACK, session); } @Override diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MaNameUtil.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MaNameUtil.java index 16e968691d..e18abe6205 100644 --- a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MaNameUtil.java +++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MaNameUtil.java @@ -30,11 +30,14 @@ import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenanc import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.DefaultNameRfc2685VpnId; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.DefaultNameUint16; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.DefaultNameY1731Icc; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.NameCharacterString; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.manameandtypecombo.nameprimaryvid.NamePrimaryVidUnion; import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.Identifier45; /** - * This is a workaround for Checkstyle issue. + * Utility for translating between Maintenance Association names in the CFM API model and the device YANG. + * + * This has to be in a separate file as a workaround for Checkstyle issue. * https://github.com/checkstyle/checkstyle/issues/3850 * There are two types of DefaultNameCharacterString - one for MA and another for MD * Putting both together in a file means that the full path has to be given which @@ -46,6 +49,12 @@ public final class MaNameUtil { //Hidden } + /** + * Convert CFM API MA identifier to the YANG model MA identifier. + * @param maId Maintenance Association ID in CFM API + * @return Maintenance Association ID in YANG API + * @throws CfmConfigException If there's a problem with the name + */ public static MaNameAndTypeCombo getYangMaNameFromApiMaId(MaIdShort maId) throws CfmConfigException { MaNameAndTypeCombo maName; @@ -70,4 +79,52 @@ public final class MaNameUtil { } return maName; } + + /** + * Convert YANG API MA identifier to the CFM API MA identifier. + * @param nameAndTypeCombo Maintenance Association ID in YANG API + * @return Maintenance Association ID in CFM API + */ + public static MaIdShort getApiMaIdFromYangMaName(MaNameAndTypeCombo nameAndTypeCombo) { + MaIdShort maId; + if (nameAndTypeCombo instanceof DefaultNameCharacterString) { + maId = MaIdCharStr.asMaId( + ((DefaultNameCharacterString) nameAndTypeCombo).name().string()); + } else if (nameAndTypeCombo instanceof DefaultNamePrimaryVid) { + if (((DefaultNamePrimaryVid) nameAndTypeCombo).namePrimaryVid().enumeration() != null) { + maId = MaIdPrimaryVid.asMaId( + ((DefaultNamePrimaryVid) nameAndTypeCombo).namePrimaryVid().enumeration().name()); + } else if (((DefaultNamePrimaryVid) nameAndTypeCombo).namePrimaryVid().vlanIdType() != null) { + maId = MaIdPrimaryVid.asMaId( + ((DefaultNamePrimaryVid) nameAndTypeCombo).namePrimaryVid().vlanIdType().uint16()); + } else { + throw new IllegalArgumentException("Unexpected primaryVid for " + + "MaNameAndTypeCombo: " + nameAndTypeCombo.toString()); + } + } else if (nameAndTypeCombo instanceof DefaultNameUint16) { + maId = MaId2Octet.asMaId(((DefaultNameUint16) nameAndTypeCombo).nameUint16()); + + } else if (nameAndTypeCombo instanceof DefaultNameRfc2685VpnId) { + maId = MaIdRfc2685VpnId.asMaIdHex( + HexString.toHexString( + ((DefaultNameRfc2685VpnId) nameAndTypeCombo).nameRfc2685VpnId())); + } else if (nameAndTypeCombo instanceof DefaultNameY1731Icc) { + maId = MaIdIccY1731.asMaId(((DefaultNameY1731Icc) nameAndTypeCombo).nameY1731Icc().string()); + + } else { + throw new IllegalArgumentException("Unexpected type for " + + "MaNameAndTypeCombo: " + nameAndTypeCombo.toString()); + } + + return maId; + } + + /** + * Cast the YANG generic type of MaNameAndTypeCombo specifically to char string. + * @param maName a YANG generic MaNameAndTypeCombo + * @return a YANG specific MaNameAndTypeCombo for Char string + */ + public static NameCharacterString cast(MaNameAndTypeCombo maName) { + return (NameCharacterString) maName; + } } diff --git a/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MdNameUtil.java b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MdNameUtil.java new file mode 100644 index 0000000000..ba0cc96a39 --- /dev/null +++ b/drivers/microsemi/src/main/java/org/onosproject/drivers/microsemi/yang/utils/MdNameUtil.java @@ -0,0 +1,139 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.onosproject.drivers.microsemi.yang.utils; + +import org.onlab.packet.IpAddress; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; +import org.onosproject.yang.gen.v1.ietfinettypes.rev20130715.ietfinettypes.DomainName; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.MdNameAndTypeCombo; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultMacAddressAndUint; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameCharacterString; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameDomainName; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.DefaultNameNone; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.MacAddressAndUint; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.NameCharacterString; +import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.mdnameandtypecombo.namedomainname.NameDomainNameUnion; +import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.Identifier45; +import org.onosproject.yang.gen.v1.mseatypes.rev20160229.mseatypes.MacAddressAndUintStr; + +/** + * Utility for translating between Maintenance Domain names in the CFM API model and the device YANG. + * + * This has to be in a separate file as a workaround for Checkstyle issue. + * https://github.com/checkstyle/checkstyle/issues/3850 + * There are two types of DefaultNameCharacterString - one for MA and another for MD + * Putting both together in a file means that the full path has to be given which + * will then fail checkstyle + */ +public final class MdNameUtil { + + private MdNameUtil() { + //Hidden + } + + /** + * Convert CFM API MD identifier to the YANG model MD identifier. + * @param mdId Maintenance Domain ID in CFM API + * @return Maintenance Domain ID in YANG API + * @throws CfmConfigException If there's a problem with the name + */ + public static MdNameAndTypeCombo getYangMdNameFromApiMdId(MdId mdId) + throws CfmConfigException { + MdNameAndTypeCombo mdName; + if (mdId instanceof MdIdDomainName) { + boolean isIpAddr = false; + try { + if (IpAddress.valueOf(mdId.mdName()) != null) { + isIpAddr = true; + } + } catch (IllegalArgumentException e) { + //continue + } + if (isIpAddr) { + mdName = new DefaultNameDomainName(); + ((DefaultNameDomainName) mdName).nameDomainName(NameDomainNameUnion.of( + org.onosproject.yang.gen.v1.ietfinettypes.rev20130715.ietfinettypes. + IpAddress.fromString(mdId.mdName()))); + } else { + mdName = new DefaultNameDomainName(); + ((DefaultNameDomainName) mdName).nameDomainName(NameDomainNameUnion + .of(DomainName.fromString(mdId.mdName()))); + } + } else if (mdId instanceof MdIdMacUint) { + mdName = new DefaultMacAddressAndUint(); + ((DefaultMacAddressAndUint) mdName).nameMacAddressAndUint(MacAddressAndUintStr.fromString(mdId.mdName())); + } else if (mdId instanceof MdIdNone) { + mdName = new DefaultNameNone(); + } else if (mdId instanceof MdIdCharStr) { + mdName = new DefaultNameCharacterString(); + ((DefaultNameCharacterString) mdName).name(Identifier45.fromString(mdId.mdName())); + } else { + throw new CfmConfigException("Unexpected error creating MD " + + mdId.getClass().getSimpleName()); + } + return mdName; + } + + /** + * Convert YANG API MD identifier to the CFM API MD identifier. + * @param nameAndTypeCombo Maintenance Domain ID in YANG API + * @return Maintenance Domain ID in CFM API + */ + public static MdId getApiMdIdFromYangMdName(MdNameAndTypeCombo nameAndTypeCombo) { + MdId mdId; + if (nameAndTypeCombo instanceof DefaultNameDomainName) { + NameDomainNameUnion domainName = + ((DefaultNameDomainName) nameAndTypeCombo).nameDomainName(); + if (domainName.ipAddress() != null) { + mdId = MdIdDomainName.asMdId(domainName.ipAddress().toString()); + } else if (domainName.domainName() != null) { + mdId = MdIdDomainName.asMdId(domainName.domainName().string()); + } else { + throw new IllegalArgumentException("Unexpected domainName for " + + "MdNameAndTypeCombo: " + nameAndTypeCombo.toString()); + } + } else if (nameAndTypeCombo instanceof DefaultNameCharacterString) { + mdId = MdIdCharStr.asMdId( + ((NameCharacterString) nameAndTypeCombo).name().string()); + + } else if (nameAndTypeCombo instanceof DefaultMacAddressAndUint) { + mdId = MdIdMacUint.asMdId( + ((MacAddressAndUint) nameAndTypeCombo).nameMacAddressAndUint().string()); + + } else if (nameAndTypeCombo instanceof DefaultNameNone) { + mdId = MdIdNone.asMdId(); + } else { + throw new IllegalArgumentException("Unexpected type for " + + "MdNameAndTypeCombo: " + nameAndTypeCombo.toString()); + } + + return mdId; + } + + /** + * Cast the YANG generic type of MdNameAndTypeCombo specifically to char string. + * @param maName a YANG generic MdNameAndTypeCombo + * @return a YANG specific MdNameAndTypeCombo for Char string + */ + public static NameCharacterString cast(MdNameAndTypeCombo maName) { + return (NameCharacterString) maName; + } +} diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammableTest.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammableTest.java index 20e80a12fa..1f8763424e 100644 --- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammableTest.java +++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/EA1000CfmMepProgrammableTest.java @@ -20,11 +20,13 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.onosproject.drivers.microsemi.yang.utils.MdNameUtil.getYangMdNameFromApiMdId; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.onosproject.drivers.microsemi.yang.utils.MaNameUtil; +import org.onosproject.drivers.microsemi.yang.utils.MdNameUtil; import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepLbCreate; import org.onosproject.incubator.net.l2monitoring.cfm.Mep.Priority; import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry; @@ -38,21 +40,24 @@ import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.MdNameAndTypeCombo; import org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm.maintenancedomain.maintenanceassociation.MaNameAndTypeCombo; import java.util.BitSet; +import java.util.Optional; /** * Test of the CFM implementation on EA1000 through the incubator/net/l2monitoring interface. */ public class EA1000CfmMepProgrammableTest { - EA1000CfmMepProgrammable cfmProgrammable; public static final MdId MD_ID_1 = MdIdCharStr.asMdId("md-1"); public static final MaIdShort MA_ID_11 = MaIdCharStr.asMaId("ma-1-1"); public static final MepId MEP_111 = MepId.valueOf((short) 1); public static final MepId MEP_112 = MepId.valueOf((short) 2); + private CfmMepProgrammable cfmProgrammable; + @Before public void setUp() throws Exception { cfmProgrammable = new EA1000CfmMepProgrammable(); @@ -67,12 +72,6 @@ public class EA1000CfmMepProgrammableTest { fail("Not yet implemented"); } - @Ignore - @Test - public void testGetAllMeps() { - fail("Not yet implemented"); - } - @Test public void testGetMep() throws CfmConfigException { MepEntry mepEntry = cfmProgrammable.getMep(MD_ID_1, MA_ID_11, MEP_111); @@ -143,7 +142,107 @@ public class EA1000CfmMepProgrammableTest { */ @Test public void testDeleteMep() throws CfmConfigException { - assertTrue(cfmProgrammable.deleteMep(MD_ID_1, MA_ID_11, MEP_111)); + assertTrue(cfmProgrammable.deleteMep(MD_ID_1, MA_ID_11, MEP_111, Optional.empty())); + } + + /** + * Create the MD md-1 on the device. + * This will retrieve the MD from the MockCfmMdService and will create it + * and its MA on the device + * Depends on sampleXmlRegexCreateMseaCfmMa + */ + @Test + public void testCreateMaintenanceDomainOnDevice() throws CfmConfigException { + boolean success = + cfmProgrammable.createMdOnDevice(MdIdCharStr.asMdId("md-1")); + assertTrue(success); + } + + /** + * Create the MD md-2 on the device. + * This will retrieve the MD from the MockCfmMdService and will create it on + * the device. This MD has no MA + * Depends on sampleXmlRegexCreateMseaCfmMa + */ + @Test + public void testCreateMaintenanceDomainOnDevice2() throws CfmConfigException { + boolean success = + cfmProgrammable.createMdOnDevice(MdIdCharStr.asMdId("md-2")); + assertTrue(success); + } + + /** + * Delete the MD md-1 on the device. + * This will retrieve the MD from the MockCfmMdService and will delete it on + * the device. + * Depends on sampleXmlRegexCreateMseaCfmMa + */ + @Test + public void testDeleteMaintenanceDomainOnDevice() throws CfmConfigException { + boolean success = + cfmProgrammable.deleteMdOnDevice(MdIdCharStr.asMdId("md-1"), Optional.empty()); + assertTrue(success); + } + + + /** + * Create the MA ma-1-1 on the device. + * This will retrieve the MA from the MockCfmMdService and will create it + * on the device under md-1 + * Depends on sampleXmlRegexCreateMseaCfmMa + */ + @Test + public void testCreateMaintenanceAssociationOnDevice() throws CfmConfigException { + boolean success = + cfmProgrammable.createMaOnDevice( + MdIdCharStr.asMdId("md-1"), MaIdCharStr.asMaId("ma-1-1")); + assertTrue(success); + } + + /** + * Delete the MD md-1 on the device. + * This will retrieve the MD from the MockCfmMdService and will delete it on + * the device. + * Depends on sampleXmlRegexCreateMseaCfmMa + */ + @Test + public void testDeleteMaintenanceAssociationOnDevice() throws CfmConfigException { + boolean success = + cfmProgrammable.deleteMaOnDevice( + MdIdCharStr.asMdId("md-1"), + MaIdCharStr.asMaId("ma-1-1"), + Optional.empty()); + assertTrue(success); + } + + /** + * Create the Remote Mep 10001 in ma-1-1 on the device. + * This will retrieve the MA from the MockCfmMdService and will create the + * new remote mep under it on the device + * Depends on sampleXmlRegexCreateMseaCfmMa + */ + @Test + public void testCreateRemoteMepOnDevice() throws CfmConfigException { + boolean success = + cfmProgrammable.createMaRemoteMepOnDevice( + MdIdCharStr.asMdId("md-1"), MaIdCharStr.asMaId("ma-1-1"), + MepId.valueOf((short) 1001)); + assertTrue(success); + } + + /** + * Delete the Remote Mep 1002 in ma-1-1 on the device. + * This will retrieve the MA from the MockCfmMdService and will delete the + * existing remote mep under it on the device + * Depends on sampleXmlRegexCreateMseaCfmMa + */ + @Test + public void testDeleteRemoteMepOnDevice() throws CfmConfigException { + boolean success = + cfmProgrammable.deleteMaRemoteMepOnDevice( + MdIdCharStr.asMdId("md-1"), MaIdCharStr.asMaId("ma-1-1"), + MepId.valueOf((short) 1001)); + assertTrue(success); } /** @@ -167,25 +266,21 @@ public class EA1000CfmMepProgrammableTest { cfmProgrammable.abortLoopback(MD_ID_1, MA_ID_11, MEP_111); } -// @Test -// public void testTransmitLinktrace() { -// fail("Not yet implemented"); -// } + @Ignore + @Test + public void testTransmitLinktrace() { + fail("Not yet implemented"); + } @Test public void testGetYangMdNameFromApiMdId() throws CfmConfigException { - MdNameAndTypeCombo name = EA1000CfmMepProgrammable - .getYangMdNameFromApiMdId(MdIdCharStr.asMdId("md-1")); + MdNameAndTypeCombo name = getYangMdNameFromApiMdId(MdIdCharStr.asMdId("md-1")); assertEquals(org.onosproject.yang.gen.v1.mseacfm.rev20160229.mseacfm.mefcfm .maintenancedomain.mdnameandtypecombo .DefaultNameCharacterString.class, name.getClass()); -//There's a problem with checkstyle for typecast on really long paths -// assertEquals("md-1", ((org.onosproject.yang.gen.v1.http.www.microsemi.com -// .microsemi.edge.assure.msea.cfm.rev20160229.mseacfm.mefcfm -// .maintenancedomain.mdnameandtypecombo -// .DefaultNameCharacterString) name).name().string()); + assertEquals("md-1", MdNameUtil.cast(name).name().string()); } @Test @@ -196,11 +291,6 @@ public class EA1000CfmMepProgrammableTest { .maintenancedomain.maintenanceassociation.manameandtypecombo .DefaultNameCharacterString.class, name.getClass()); -//There's a problem with checkstyle for typecast on really long paths -// assertEquals("ma-1-1", ((org.onosproject.yang.gen.v1.http.www.microsemi.com -// .microsemi.edge.assure.msea.cfm.rev20160229.mseacfm.mefcfm -// .maintenancedomain.maintenanceassociation.manameandtypecombo -// .DefaultNameCharacterString) name).name().string()); + assertEquals("ma-1-1", MaNameUtil.cast(name).name().string()); } - } diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java index 58a8a5b090..742f8c9288 100644 --- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java +++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/MockEa1000DriverHandler.java @@ -20,6 +20,7 @@ import java.util.Map; import org.onosproject.core.CoreService; import org.onosproject.drivers.microsemi.yang.MockCfmMdService; +import org.onosproject.drivers.microsemi.yang.MockCfmMepService; import org.onosproject.drivers.microsemi.yang.MockMseaCfmManager; import org.onosproject.drivers.microsemi.yang.MockMseaSaFilteringManager; import org.onosproject.drivers.microsemi.yang.MockMseaUniEvcServiceManager; @@ -31,6 +32,7 @@ import org.onosproject.drivers.netconf.MockCoreService; import org.onosproject.drivers.netconf.MockNetconfController; import org.onosproject.drivers.netconf.MockNetconfDevice; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; import org.onosproject.net.DeviceId; import org.onosproject.net.driver.Behaviour; import org.onosproject.net.driver.DefaultDriver; @@ -59,6 +61,7 @@ public class MockEa1000DriverHandler implements DriverHandler { private MockMseaUniEvcServiceManager mseaUniEvcService; private MockMseaCfmManager mseaCfmService; private MockCfmMdService mockMdService; + private MockCfmMepService mockMepService; private CoreService coreService; public MockEa1000DriverHandler() throws NetconfException { @@ -92,6 +95,9 @@ public class MockEa1000DriverHandler implements DriverHandler { mockMdService = new MockCfmMdService(); mockMdService.activate(); + mockMepService = new MockCfmMepService(); + mockMepService.activate(); + coreService = new MockCoreService(); coreService.registerApplication(MICROSEMI_DRIVERS); } @@ -131,6 +137,10 @@ public class MockEa1000DriverHandler implements DriverHandler { } else if (serviceClass.equals(CfmMdService.class)) { return (T) mockMdService; + + } else if (serviceClass.equals(CfmMepService.class)) { + return (T) mockMepService; + } return null; diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMdService.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMdService.java index ba6d6561eb..3d0fb5f07d 100644 --- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMdService.java +++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMdService.java @@ -15,6 +15,8 @@ */ package org.onosproject.drivers.microsemi.yang; +import org.onlab.packet.VlanId; +import org.onosproject.incubator.net.l2monitoring.cfm.DefaultComponent; import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceAssociation; import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain; import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation; @@ -22,6 +24,7 @@ import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; import org.onosproject.incubator.net.l2monitoring.cfm.impl.CfmMdManager; import static org.easymock.EasyMock.*; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; @@ -29,6 +32,9 @@ import org.onosproject.incubator.net.l2monitoring.cfm.service.MdStore; import java.util.Optional; +/** + * Supports testing of services that reply on the CfmMdService. + */ public class MockCfmMdService extends CfmMdManager { @Override @@ -40,6 +46,11 @@ public class MockCfmMdService extends CfmMdManager { .builder(MaIdCharStr.asMaId("ma-1-1"), 6) .maNumericId((short) 1) .ccmInterval(MaintenanceAssociation.CcmInterval.INTERVAL_3MS) + .addToRemoteMepIdList(MepId.valueOf((short) 10)) + .addToRemoteMepIdList(MepId.valueOf((short) 20)) + .addToComponentList( + DefaultComponent.builder(1) + .addToVidList(VlanId.vlanId((short) 101)).build()) .build(); MdId md1Name = MdIdCharStr.asMdId("md-1"); @@ -50,14 +61,25 @@ public class MockCfmMdService extends CfmMdManager { .addToMaList(ma) .build(); + MdId md2Name = MdIdCharStr.asMdId("md-2"); + MaintenanceDomain md2 = DefaultMaintenanceDomain + .builder(md1Name) + .mdNumericId((short) 2) + .mdLevel(MaintenanceDomain.MdLevel.LEVEL2) + .build(); + expect(store.createUpdateMaintenanceDomain(md1)) .andReturn(true); + expect(store.createUpdateMaintenanceDomain(md2)) + .andReturn(true); expect(store.getMaintenanceDomain(md1Name)) .andReturn(Optional.of(md1)).anyTimes(); + expect(store.getMaintenanceDomain(md2Name)) + .andReturn(Optional.of(md2)).anyTimes(); replay(store); } catch (CfmConfigException e) { - throw new IllegalArgumentException("Error creating Md md-1 for test"); + throw new IllegalArgumentException("Error creating MDs for test", e); } } } diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMepService.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMepService.java new file mode 100644 index 0000000000..c00fc4e1aa --- /dev/null +++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockCfmMepService.java @@ -0,0 +1,137 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.onosproject.drivers.microsemi.yang; + +import org.onlab.junit.TestUtils; +import org.onlab.packet.ChassisId; +import org.onosproject.common.event.impl.TestEventDispatcher; +import org.onosproject.core.CoreServiceAdapter; +import org.onosproject.core.IdGenerator; +import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep; +import org.onosproject.incubator.net.l2monitoring.cfm.Mep; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId; +import org.onosproject.incubator.net.l2monitoring.cfm.impl.CfmMdManager; +import org.onosproject.incubator.net.l2monitoring.cfm.impl.CfmMepManager; +import org.onosproject.incubator.net.l2monitoring.cfm.impl.TestCfmMepProgrammable; +import org.onosproject.incubator.net.l2monitoring.cfm.impl.TestDeviceDiscoveryBehavior; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable; +import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStore; +import org.onosproject.incubator.net.l2monitoring.soam.SoamDmProgrammable; +import org.onosproject.incubator.net.l2monitoring.soam.impl.TestSoamDmProgrammable; +import org.onosproject.net.AnnotationKeys; +import org.onosproject.net.DefaultAnnotations; +import org.onosproject.net.DefaultDevice; +import org.onosproject.net.Device; +import org.onosproject.net.DeviceId; +import org.onosproject.net.PortNumber; +import org.onosproject.net.device.DeviceDescriptionDiscovery; +import org.onosproject.net.device.DeviceService; +import org.onosproject.net.driver.Behaviour; +import org.onosproject.net.driver.DefaultDriver; +import org.onosproject.net.driver.Driver; +import org.onosproject.net.driver.DriverService; +import org.onosproject.net.provider.ProviderId; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.atomic.AtomicLong; + +import static org.easymock.EasyMock.createMock; +import static org.easymock.EasyMock.expect; +import static org.onosproject.net.NetTestTools.injectEventDispatcher; + +/** + * Supports testing of services that reply on the CfmMepService. + */ +public class MockCfmMepService extends CfmMepManager { + private static final String TEST_MFR = "testMfr"; + private static final String TEST_HW_VERSION = "testHwVersion"; + private static final String TEST_SW_VERSION = "testSwVersion"; + private static final String TEST_SN = "testSn"; + private static final String TEST_DRIVER = "testDriver"; + protected static final DeviceId DEVICE_ID1 = DeviceId.deviceId("netconf:1.2.3.4:830"); + + + private final DriverService driverService = createMock(DriverService.class); + + private Device device1; + private Driver testDriver; + + + @Override + public void activate() { + mepStore = createMock(MepStore.class); + cfmMdService = new MockCfmMdService(); + deviceService = createMock(DeviceService.class); + ((CfmMdManager) cfmMdService).activate(); + + device1 = new DefaultDevice( + ProviderId.NONE, DEVICE_ID1, Device.Type.SWITCH, + TEST_MFR, TEST_HW_VERSION, TEST_SW_VERSION, TEST_SN, + new ChassisId(1), + DefaultAnnotations.builder().set(AnnotationKeys.DRIVER, TEST_DRIVER).build()); + + Map, Class> behaviours = new HashMap<>(); + behaviours.put(DeviceDescriptionDiscovery.class, TestDeviceDiscoveryBehavior.class); + behaviours.put(CfmMepProgrammable.class, TestCfmMepProgrammable.class); + behaviours.put(SoamDmProgrammable.class, TestSoamDmProgrammable.class); + + TestUtils.setField(this, "coreService", new TestCoreService()); + TestUtils.setField(this, "deviceService", deviceService); + injectEventDispatcher(this, new TestEventDispatcher()); + + testDriver = new DefaultDriver( + TEST_DRIVER, new ArrayList(), + TEST_MFR, TEST_HW_VERSION, TEST_SW_VERSION, + behaviours, new HashMap<>()); + + try { + Mep mep1 = DefaultMep.builder( + MepId.valueOf((short) 10), + DEVICE_ID1, + PortNumber.P0, + Mep.MepDirection.UP_MEP, + MdIdCharStr.asMdId("md-1"), + MaIdCharStr.asMaId("ma-1-1")) + .build(); + + expect(mepStore.getMep(new MepKeyId(mep1))).andReturn(Optional.of(mep1)).anyTimes(); + } catch (CfmConfigException e) { + throw new IllegalArgumentException("Error creating MEPs for test", e); + } + } + + private class TestCoreService extends CoreServiceAdapter { + + @Override + public IdGenerator getIdGenerator(String topic) { + return new IdGenerator() { + private AtomicLong counter = new AtomicLong(0); + + @Override + public long getNewId() { + return counter.getAndIncrement(); + } + }; + } + } +} diff --git a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockNetconfSessionEa1000.java b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockNetconfSessionEa1000.java index b9d4342250..6bf67c060d 100644 --- a/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockNetconfSessionEa1000.java +++ b/drivers/microsemi/src/test/java/org/onosproject/drivers/microsemi/yang/MockNetconfSessionEa1000.java @@ -478,11 +478,12 @@ public class MockNetconfSessionEa1000 extends NetconfSessionAdapter { + "()\\R?" + "()\\R?" - + "()?\\R?" - + "()([a-zA-Z0-9\\-:\\.]){1,48}()\\R?" + + "([0-9]{1,5})?\\R?" + + "(()([a-zA-Z0-9\\-:\\.]){1,48}())?\\R?" + "()\\R?" - + "()?\\R?" - + "()([a-zA-Z0-9\\-:\\.]){1,48}()\\R?" + + "([0-9]{1,5})?\\R?" + + "(([a-zA-Z0-9\\-:\\.]{1,48})|" + + "([0-9]{1,4}))?\\R?" + "()\\R?" + "()[0-9]{1,4}()\\R?" + "()\\R?" @@ -492,6 +493,85 @@ public class MockNetconfSessionEa1000 extends NetconfSessionAdapter { + "()\\R?" + "()\\R?()\\R?(]]>){2}", Pattern.DOTALL); + //For testGetConfigMseaCfmEssentials + private Pattern sampleXmlRegexCreateMseaCfmMa = + Pattern.compile("(<\\?xml).*()\\R?" + + "(\\R?\\R?)\\R?" + + "()\\R?" + + "()\\R?" + + "()([0-9]){1,4}()\\R?" + + "(()([0-9]){1}())?\\R?" + + "(()([a-zA-Z0-9\\-:\\.]){1,48}())?\\R?" + + "(()\\R?" + + "()([0-9]){1,4}()\\R?" + + "(()(3.3ms)())?\\R?" + + "(()([0-9]){1,4}())*\\R?" + + "((()([a-zA-Z0-9\\-:\\.]){1,48}())|" + + "(()([0-9]){1,4}()))?\\R?" + + "(()\\R?" + + "()([0-9]){1,4}()\\R?" + + "())?\\R?" + + "())*\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?()\\R?(]]>){2}", Pattern.DOTALL); + + //For testGetConfigMseaCfmEssentials + private Pattern sampleXmlRegexDeleteMseaCfmMa = + Pattern.compile("(<\\?xml).*()\\R?" + + "(\\R?\\R?)\\R?" + + "()\\R?" + + "()\\R?" + + "(()|(()([0-9]){1,4}()))?\\R?" + + "(()([a-zA-Z0-9\\-:\\.]){1,48}())?\\R?" + + "()\\R?" + + "(()|(()([0-9]){1,4}()))?\\R?" + + "((()([a-zA-Z0-9\\-:\\.]){1,48}())|" + + "(()([0-9]){1,4}()))?\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?()\\R?(]]>){2}", Pattern.DOTALL); + + //For testDeleteMseaMepRemoteMep + private Pattern sampleXmlRegexDeleteMseaCfmRmep = + Pattern.compile("(<\\?xml).*()\\R?" + + "(\\R?\\R?)\\R?" + + "()\\R?" + + "()\\R?" + + "(()[0-9]{1,4}())?\\R?" + + "(()([a-zA-Z0-9\\-:\\.]){1,48}())?\\R?" + + "()\\R?" + + "(()[0-9]{1,4}())?\\R?" + + "(()([0-9]){1,4}())*\\R?" + + "((()([a-zA-Z0-9\\-:\\.]){1,48}())|" + + "(()([0-9]){1,4}()))?\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?()\\R?(]]>){2}", Pattern.DOTALL); + + //For testDeleteMseaMd + private Pattern sampleXmlRegexDeleteMseaCfmMd = + Pattern.compile("(<\\?xml).*()\\R?" + + "(\\R?\\R?)\\R?" + + "()\\R?" + + "()\\R?" + + "(()|(([0-9]){1,4}()))?\\R?" + + "((()([a-zA-Z0-9\\-:\\.]){1,48}())|\\R?" + + "(()([a-zA-Z0-9\\-\\.]){1,48}()))?\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?" + + "()\\R?()\\R?(]]>){2}", Pattern.DOTALL); + private Pattern sampleXmlRegexTransmitLoopback = Pattern.compile("(<\\?xml).*( { /** * Get a list of all of the Maintenance Domains on the system. diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepEvent.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepEvent.java index 26abc45446..cb96b5230e 100644 --- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepEvent.java +++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepEvent.java @@ -16,12 +16,12 @@ package org.onosproject.incubator.net.l2monitoring.cfm.service; import org.onosproject.event.AbstractEvent; -import org.onosproject.incubator.net.l2monitoring.cfm.Mep; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId; /** * Event related to the maintenance of CFM MEPs. */ -public class CfmMepEvent extends AbstractEvent { +public class CfmMepEvent extends AbstractEvent { /** * Type of Mep events. @@ -52,10 +52,9 @@ public class CfmMepEvent extends AbstractEvent { * Creates an event of a given type and for the specified Mep and the current time. * * @param type Mep event type - * @param mep event Mep subject + * @param mepKeyId event Mep subject */ - protected CfmMepEvent(Type type, Mep mep) { - super(type, mep); - // TODO Auto-generated constructor stub + public CfmMepEvent(Type type, MepKeyId mepKeyId) { + super(type, mepKeyId); } } diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepProgrammable.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepProgrammable.java index b92777b2d8..1b902cb24f 100644 --- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepProgrammable.java +++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepProgrammable.java @@ -15,14 +15,95 @@ */ package org.onosproject.incubator.net.l2monitoring.cfm.service; +import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; import org.onosproject.net.driver.HandlerBehaviour; +import java.util.Optional; + /** * Behaviour that allows Layer 2 Monitoring as defined in IEEE 802.1Q be implemented by devices. * * Has all of the same methods as - * {@link org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService} so reuse that + * {@link CfmMepServiceBase} so reuse that */ -public interface CfmMepProgrammable extends HandlerBehaviour, CfmMepService { +public interface CfmMepProgrammable extends HandlerBehaviour, CfmMepServiceBase { + + + /** + * Allows an MD and children MAs to be created on a device. + * A convenience method that allows an MD to be created on a device + * This is a preparation mechanism. If this was not called the MD would be created + * at the time of provisioning a MEP anyway + * @param mdId The ID of the MD to create - the details will be got from the CfmMdService + * @return true when created. false if it already existed + * @throws CfmConfigException If the MD already exists + */ + boolean createMdOnDevice(MdId mdId) throws CfmConfigException; + + /** + * Allows an MA to be created on an existing MD on a device. + * A convenience method that allows an MA to be created + * This is a preparation mechanism. If this was not called the MA would be created + * at the time of provisioning a MEP anyway. Also MAs can be created when they + * are contained in an MD using the createMdOnDevice method + * @param mdId The identifier of the MD to create the MA in + * @param maId The identifier of the MA to create - the details will be retrieved from the CfmMdService + * @return true when created. false if it already existed + * @throws CfmConfigException If the MD already exists + */ + boolean createMaOnDevice(MdId mdId, MaIdShort maId) throws CfmConfigException; + + /** + * Allows a MD and its MAs to be deleted from a device. + * A convenience method that allows an MD that has been provisioned on a + * device to be removed. + * All Meps must be removed first unless they are Meps unknown to ONOS. + * This is a cleanup mechanism. Deleting Meps in the normal way does not celan + * up MDs and MAs + * @param mdId The identifier of the MD + * @param oldMd The MaintenanceDomain that is being deleted + * @return true when deleted. false if it did not exist + * @throws CfmConfigException If the MD has any MAs that have MEPs then this will fail + */ + boolean deleteMdOnDevice(MdId mdId, Optional oldMd) throws CfmConfigException; + + /** + * Allows an MA to be deleted from an MD on a device. + * A convenience method that allows an MA that has been provisioned on a + * device to be removed. + * All Meps must be removed first unless they are Meps unknown to ONOS. + * This is a cleanup mechanism. Deleting Meps in the normal way does not celan + * up MDs and MAs + * @param mdId The identifier of the MD + * @param maId The identifier of the MA + * @param oldMd The MaintenanceDomain from which the MA is being deleted + * @return true when deleted. false if it did not exist + * @throws CfmConfigException If the MA has any MEPs then this will fail + */ + boolean deleteMaOnDevice(MdId mdId, MaIdShort maId, + Optional oldMd) throws CfmConfigException; + + /** + * Creates a Remote Mep entry on an MA. + * @param mdId The identifier of the MD + * @param maId The identifier of the MA + * @param remoteMep The remote Mep Id to remove from the MA + * @return true when deleted. false if it did not exist + * @throws CfmConfigException If the MA does not exist this will fail + */ + boolean createMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException; + + /** + * Deletes a Remote Mep entry on an MA. + * @param mdId The identifier of the MD + * @param maId The identifier of the MA + * @param remoteMep The remote Mep Id to remove from the MA + * @return true when deleted. false if it did not exist + * @throws CfmConfigException If the MA does not exist this will fail + */ + boolean deleteMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException; } diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepService.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepService.java index c0c2f92548..c774c65869 100644 --- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepService.java +++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepService.java @@ -17,13 +17,12 @@ package org.onosproject.incubator.net.l2monitoring.cfm.service; import java.util.Collection; +import org.onosproject.event.ListenerService; import org.onosproject.incubator.net.l2monitoring.cfm.Mep; import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry; -import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate; -import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; -import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.net.DeviceId; /** * For the management of Maintenance Association Endpoints. @@ -31,7 +30,8 @@ import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; * These are dependent on the Maintenance Domain service which maintains the * Maintenance Domain and Maintenance Associations */ -public interface CfmMepService { +public interface CfmMepService + extends ListenerService, CfmMepServiceBase { /** * Retrieve all {@link org.onosproject.incubator.net.l2monitoring.cfm.MepEntry}(s) belonging to an MA. * @param mdName A Maintenance Domain @@ -43,67 +43,13 @@ public interface CfmMepService { throws CfmConfigException; /** - * Retrieve a named {@link org.onosproject.incubator.net.l2monitoring.cfm.MepEntry} belonging to an MA. - * @param mdName A Maintenance Domain - * @param maName A Maintetance Association in the MD - * @param mepId A Mep Id - * @return A MEP Entry or null if none found - * @throws CfmConfigException If there a problem with the MD, MA or MEP - */ - MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId) - throws CfmConfigException; - - /** - * Delete a named {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep} belonging to an MA. - * @param mdName A Maintenance Domain - * @param maName A Maintetance Association in the MD - * @param mepId A Mep Id - * @return true if the MEP was deleted successfully. false if it was not found + * Retrieve all {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep}(s) belonging to an MA. + * Note: This just returns the configuration part of the Mep, not the MepEntry + * which contains config and state + * @param deviceId A device id + * @return A collection of MEP Entries * @throws CfmConfigException If there a problem with the MD or MA */ - boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId) + Collection getAllMepsByDevice(DeviceId deviceId) throws CfmConfigException; - - /** - * Create a named {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep} on an MA. - * @param mdName A Maintenance Domain - * @param maName A Maintetance Association in the MD - * @param mep A Mep object - * @return False if it was created successfully. True if the object already exists. - * @throws CfmConfigException If there a problem with the MD, MA or MEP - */ - boolean createMep(MdId mdName, MaIdShort maName, Mep mep) - throws CfmConfigException; - - /** - * Create a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry Loopback} session on the named Mep. - * @param mdName A Maintenance Domain - * @param maName A Maintetance Association in the MD - * @param mepId A Mep Id - * @param lbCreate The Loopback session details - * @throws CfmConfigException If there a problem with the MD, MA or MEP - */ - void transmitLoopback(MdId mdName, MaIdShort maName, MepId mepId, - MepLbCreate lbCreate) throws CfmConfigException; - - /** - * Abort a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry Loopback} session on the named Mep. - * @param mdName A Maintenance Domain - * @param maName A Maintetance Association in the MD - * @param mepId A Mep Id - * @throws CfmConfigException If there a problem with the MD, MA or MEP - */ - void abortLoopback(MdId mdName, MaIdShort maName, MepId mepId) - throws CfmConfigException; - - /** - * Create a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLtEntry Linktrace} session on the named Mep. - * @param mdName A Maintenance Domain - * @param maName A Maintetance Association in the MD - * @param mepId A Mep Id - * @param ltCreate The Linktrace session details - * @throws CfmConfigException If there a problem with the MD, MA or MEP - */ - void transmitLinktrace(MdId mdName, MaIdShort maName, MepId mepId, - MepLtCreate ltCreate) throws CfmConfigException; } diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepServiceBase.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepServiceBase.java new file mode 100644 index 0000000000..71e5611894 --- /dev/null +++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/CfmMepServiceBase.java @@ -0,0 +1,95 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.onosproject.incubator.net.l2monitoring.cfm.service; + +import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain; +import org.onosproject.incubator.net.l2monitoring.cfm.Mep; +import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry; +import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate; +import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; + +import java.util.Optional; + +public interface CfmMepServiceBase { + /** + * Retrieve a named {@link org.onosproject.incubator.net.l2monitoring.cfm.MepEntry} belonging to an MA. + * @param mdName A Maintenance Domain + * @param maName A Maintetance Association in the MD + * @param mepId A Mep Id + * @return A MEP Entry or null if none found + * @throws CfmConfigException If there a problem with the MD, MA or MEP + */ + MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId) + throws CfmConfigException; + + /** + * Delete a named {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep} belonging to an MA. + * @param mdName A Maintenance Domain + * @param maName A Maintetance Association in the MD + * @param mepId A Mep Id + * @param oldMd The MaintenanceDomain from which the MEP is being deleted + * @return true if the MEP was deleted successfully. false if it was not found + * @throws CfmConfigException If there a problem with the MD or MA + */ + boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId, Optional oldMd) + throws CfmConfigException; + + /** + * Create a named {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep} on an MA. + * @param mdName A Maintenance Domain + * @param maName A Maintetance Association in the MD + * @param mep A Mep object + * @return True if it was created successfully. False if the object already exists. + * @throws CfmConfigException If there a problem with the MD, MA or MEP + */ + boolean createMep(MdId mdName, MaIdShort maName, Mep mep) + throws CfmConfigException; + + /** + * Create a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry Loopback} session on the named Mep. + * @param mdName A Maintenance Domain + * @param maName A Maintetance Association in the MD + * @param mepId A Mep Id + * @param lbCreate The Loopback session details + * @throws CfmConfigException If there a problem with the MD, MA or MEP + */ + void transmitLoopback(MdId mdName, MaIdShort maName, MepId mepId, + MepLbCreate lbCreate) throws CfmConfigException; + + /** + * Abort a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLbEntry Loopback} session on the named Mep. + * @param mdName A Maintenance Domain + * @param maName A Maintetance Association in the MD + * @param mepId A Mep Id + * @throws CfmConfigException If there a problem with the MD, MA or MEP + */ + void abortLoopback(MdId mdName, MaIdShort maName, MepId mepId) + throws CfmConfigException; + + /** + * Create a {@link org.onosproject.incubator.net.l2monitoring.cfm.MepLtEntry Linktrace} session on the named Mep. + * @param mdName A Maintenance Domain + * @param maName A Maintetance Association in the MD + * @param mepId A Mep Id + * @param ltCreate The Linktrace session details + * @throws CfmConfigException If there a problem with the MD, MA or MEP + */ + void transmitLinktrace(MdId mdName, MaIdShort maName, MepId mepId, + MepLtCreate ltCreate) throws CfmConfigException; +} diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MdEvent.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MdEvent.java index e3090f7b90..49023d4c8c 100644 --- a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MdEvent.java +++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MdEvent.java @@ -15,24 +15,78 @@ */ package org.onosproject.incubator.net.l2monitoring.cfm.service; +import com.google.common.base.MoreObjects; +import org.onlab.util.Tools; import org.onosproject.event.AbstractEvent; +import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain; +import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; +import java.util.Optional; + +import static com.google.common.base.MoreObjects.toStringHelper; + /** * Event related to the maintenance of CFM MDs. */ public class MdEvent extends AbstractEvent { + private MaIdShort maId; + private MaintenanceDomain oldMd; /** * MD Event types supported. */ public enum Type { MD_ADDED, MD_REMOVED, - MD_UPDATED + MD_UPDATED, + MA_ADDED, + MA_REMOVED } public MdEvent(Type type, MdId mdId) { super(type, mdId); } + + /** + * Constructor that allows the MD to be held in the event. + * This is useful if the MD had been deleted - it will be the only way of + * retrieving some of its attributes + * @param type The type of the event + * @param mdId The ID of the MD + * @param md The whole MD + * @throws CfmConfigException if there's a problem copying MD + */ + public MdEvent(Type type, MdId mdId, MaintenanceDomain md) throws CfmConfigException { + super(type, mdId); + this.oldMd = DefaultMaintenanceDomain.builder(md).build(); + } + + public MdEvent(Type type, MdId mdId, MaintenanceDomain md, MaIdShort maId) + throws CfmConfigException { + super(type, mdId); + this.maId = maId; + this.oldMd = DefaultMaintenanceDomain.builder(md).build(); + } + + public Optional maId() { + return maId == null ? Optional.empty() : Optional.of(maId); + } + + public Optional md() { + return oldMd == null ? Optional.empty() : Optional.of(oldMd); + } + + @Override + public String toString() { + MoreObjects.ToStringHelper helper = toStringHelper(this) + .add("time", Tools.defaultOffsetDataTime(time())) + .add("type", type()) + .add("subject", subject()); + if (maId != null) { + helper = helper.add("subject2", maId); + } + return helper.toString(); + } } diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStore.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStore.java new file mode 100644 index 0000000000..ed4bb35e6d --- /dev/null +++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStore.java @@ -0,0 +1,84 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * 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.onosproject.incubator.net.l2monitoring.cfm.service; + +import org.onosproject.incubator.net.l2monitoring.cfm.Mep; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId; +import org.onosproject.net.DeviceId; +import org.onosproject.store.Store; + +import java.util.Collection; +import java.util.Optional; + +/** + * {@link org.onosproject.incubator.net.l2monitoring.cfm.Mep Maintenance Association Endpoint's} storage interface. + * Note: because the Mep is immutable if anything needs to be + * changed in it, then it must be replaced in the store. + */ +public interface MepStore extends Store { + /** + * Get a list of all of the Meps on the system. + * @return A collection Meps from the Mep Store. + */ + Collection getAllMeps(); + + /** + * Get all Meps by MD. + * @param mdName An identifier for the Maintenance Domain + * @return MEPs from the MEP Store. Empty if not found. + */ + Collection getMepsByMd(MdId mdName); + + /** + * Get all Meps by MD, MA. + * @param mdName An identifier for the Maintenance Domain + * @param maName An identifier for the Maintenance Association + * @return MEPs from the MEP Store. Empty if not found. + */ + Collection getMepsByMdMa(MdId mdName, MaIdShort maName); + + /** + * Get all Meps by DeviceId. + * @param deviceId An identifier for the Device + * @return MEPs from the MEP Store. Empty if not found. + */ + Collection getMepsByDeviceId(DeviceId deviceId); + + /** + * Get a specific Mep by its Mep key id. + * @param mepKeyId An unique identifier for the MEP + * @return A MEP from the MEP Store. Empty if not found. + */ + Optional getMep(MepKeyId mepKeyId); + + /** + * Delete a specific Mep by its identifier. + * @param mepKeyId An unique identifier for the MEP + * @return True if the Mep was found and deleted + */ + boolean deleteMep(MepKeyId mepKeyId); + + /** + * Create or replace a Mep. + * @param mepKeyId An unique identifier for the MEP + * @param mep The new MEP + * @return true if an Mep of this name already existed + */ + boolean createUpdateMep(MepKeyId mepKeyId, Mep mep); + +} diff --git a/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStoreDelegate.java b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStoreDelegate.java new file mode 100644 index 0000000000..6d5dfd3bd6 --- /dev/null +++ b/incubator/api/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/service/MepStoreDelegate.java @@ -0,0 +1,24 @@ +/* + * Copyright 2018-present Open Networking Foundation + * + * 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.onosproject.incubator.net.l2monitoring.cfm.service; + +import org.onosproject.store.StoreDelegate; + +/** + * Delegate for MEP Store. + */ +public interface MepStoreDelegate extends StoreDelegate { +} diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManager.java index 5a125f5b4c..b18d2f6764 100644 --- a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManager.java +++ b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManager.java @@ -65,10 +65,8 @@ public class CfmMdManager extends AbstractListenerManager @Activate public void activate() { appId = coreService.registerApplication(APP_ID); - eventDispatcher.addSink(MdEvent.class, listenerRegistry); store.setDelegate(delegate); - log.info("CFM Service Started"); } @@ -163,9 +161,8 @@ public class CfmMdManager extends AbstractListenerManager private class InternalStoreDelegate implements MdStoreDelegate { @Override public void notify(MdEvent event) { - log.debug("New MD event: {}", event.subject()); + log.debug("New MD event: {}", event); eventDispatcher.post(event); } } - } diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManager.java b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManager.java index 5723466b0e..e58a57091d 100644 --- a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManager.java +++ b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManager.java @@ -15,10 +15,21 @@ */ package org.onosproject.incubator.net.l2monitoring.cfm.impl; +import static org.onlab.util.Tools.groupedThreads; import static org.slf4j.LoggerFactory.getLogger; import java.util.ArrayList; import java.util.Collection; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Optional; +import java.util.Set; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; @@ -29,6 +40,8 @@ import org.apache.felix.scr.annotations.Service; import org.onosproject.core.CoreService; import org.onosproject.core.IdGenerator; import org.onosproject.event.AbstractListenerManager; +import org.onosproject.event.Event; +import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain; import org.onosproject.incubator.net.l2monitoring.cfm.Mep; import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry; import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate; @@ -36,12 +49,17 @@ import org.onosproject.incubator.net.l2monitoring.cfm.MepLtCreate; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepEvent; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepListener; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; +import org.onosproject.incubator.net.l2monitoring.cfm.service.MdEvent; +import org.onosproject.incubator.net.l2monitoring.cfm.service.MdListener; +import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStore; +import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStoreDelegate; import org.onosproject.mastership.MastershipService; import org.onosproject.net.Device; import org.onosproject.net.DeviceId; @@ -61,7 +79,8 @@ public class CfmMepManager private final Logger log = getLogger(getClass()); - private final DeviceListener deviceListener = new InternalDeviceListener(); + private InternalDeviceListener deviceListener = null; + private InternalMdListener mdListener = null; @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) protected DeviceService deviceService; @@ -78,22 +97,39 @@ public class CfmMepManager private static final int DEFAULT_POLL_FREQUENCY = 30; private int fallbackMepPollFrequency = DEFAULT_POLL_FREQUENCY; + private InternalEventHandler eventHandler = new InternalEventHandler(); + private static final Object THREAD_SCHED_LOCK = new Object(); + private static int numOfEventsQueued = 0; + private static int numOfEventsExecuted = 0; + private static int numOfHandlerExecution = 0; + private static int numOfHandlerScheduled = 0; + + private ScheduledExecutorService executorService = Executors + .newScheduledThreadPool(1, + groupedThreads("CfmMepManager", "event-%d", log)); + + @SuppressWarnings("unused") + private static ScheduledFuture eventHandlerFuture = null; + @SuppressWarnings("rawtypes") + private ConcurrentLinkedQueue eventQueue = new ConcurrentLinkedQueue<>(); + + private IdGenerator idGenerator; - //FIXME Get rid of this hack - we will use this in memory to emulate - // a store for the short term. - //Note: This is not distributed and will not work in a clustered system - //TODO Create a MepStore for this - private Collection mepCollection; + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected MepStore mepStore; + protected final MepStoreDelegate delegate = new InternalStoreDelegate(); @Activate public void activate() { - //FIXME Get rid of this local list - mepCollection = new ArrayList<>(); + mepStore.setDelegate(delegate); - eventDispatcher.addSink(CfmMepEvent.class, listenerRegistry); + deviceListener = new InternalDeviceListener(); deviceService.addListener(deviceListener); + mdListener = new InternalMdListener(); + cfmMdService.addListener(mdListener); + eventDispatcher.addSink(CfmMepEvent.class, listenerRegistry); idGenerator = coreService.getIdGenerator("mep-ids"); log.info("CFM MEP Manager Started"); } @@ -101,9 +137,12 @@ public class CfmMepManager @Deactivate public void deactivate() { deviceService.removeListener(deviceListener); + cfmMdService.removeListener(mdListener); eventDispatcher.removeSink(CfmMepEvent.class); log.info("CFM MEP Manager Stopped"); - mepCollection.clear(); + mepStore.unsetDelegate(delegate); + deviceListener = null; + mdListener = null; } @Override @@ -112,27 +151,28 @@ public class CfmMepManager //Will throw IllegalArgumentException if ma does not exist cfmMdService.getMaintenanceAssociation(mdName, maName); + Collection mepStoreCollection = mepStore.getAllMeps(); Collection mepEntryCollection = new ArrayList<>(); - for (Mep mep:mepCollection) { + for (Mep mep : mepStoreCollection) { if (mep.mdId().equals(mdName) && mep.maId().equals(maName)) { DeviceId mepDeviceId = mep.deviceId(); if (deviceService.getDevice(mepDeviceId) == null) { log.warn("Device not found/available " + mepDeviceId + - " for MEP: " + mdName + "/" + maName + "/" + mep.mepId()); + " for MEP: " + mdName + "/" + maName + "/" + mep.mepId()); continue; } else if (!deviceService.getDevice(mepDeviceId) - .is(CfmMepProgrammable.class)) { + .is(CfmMepProgrammable.class)) { throw new CfmConfigException("Device " + mepDeviceId + " does not support CfmMepProgrammable behaviour."); } log.debug("Retrieving MEP results for Mep {} in MD {}, MA {} " - + "on Device {}", mep.mepId(), mdName, maName, mepDeviceId); + + "on Device {}", mep.mepId(), mdName, maName, mepDeviceId); mepEntryCollection.add(deviceService - .getDevice(mepDeviceId) - .as(CfmMepProgrammable.class) - .getMep(mdName, maName, mep.mepId())); + .getDevice(mepDeviceId) + .as(CfmMepProgrammable.class) + .getMep(mdName, maName, mep.mepId())); } } @@ -140,75 +180,116 @@ public class CfmMepManager } @Override - public MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException { - //Will throw IllegalArgumentException if ma does not exist - cfmMdService.getMaintenanceAssociation(mdName, maName); - - for (Mep mep : mepCollection) { - if (mep.mdId().equals(mdName) && mep.maId().equals(maName) - && mep.mepId().equals(mepId)) { - - DeviceId mepDeviceId = mep.deviceId(); - if (deviceService.getDevice(mepDeviceId) == null) { - throw new CfmConfigException("Device not found " + mepDeviceId); - } else if (!deviceService.getDevice(mepDeviceId).is(CfmMepProgrammable.class)) { - throw new CfmConfigException("Device " + mepDeviceId + - " does not support CfmMepProgrammable behaviour."); - } - - log.debug("Retrieving MEP reults for Mep {} in MD {}, MA {} on Device {}", - mep.mepId(), mdName, maName, mepDeviceId); - - return deviceService.getDevice(mepDeviceId) - .as(CfmMepProgrammable.class).getMep(mdName, maName, mepId); - } - } - return null; + public Collection getAllMepsByDevice(DeviceId deviceId) throws CfmConfigException { + return mepStore.getMepsByDeviceId(deviceId); } @Override - public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException { + public MepEntry getMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException { + MepKeyId key = new MepKeyId(mdName, maName, mepId); + //Will throw IllegalArgumentException if ma does not exist cfmMdService.getMaintenanceAssociation(mdName, maName); - for (Mep mep : mepCollection) { - if (mep.mdId().equals(mdName) && mep.maId().equals(maName) - && mep.mepId().equals(mepId)) { - Device mepDevice = deviceService.getDevice(mep.deviceId()); - if (mepDevice == null || !mepDevice.is(CfmMepProgrammable.class)) { - throw new CfmConfigException("Unexpeced fault on device drier for " - + mep.deviceId()); - } - boolean deleted = false; - try { - deleted = mepDevice.as(CfmMepProgrammable.class) - .deleteMep(mdName, maName, mepId); - } catch (CfmConfigException e) { - log.warn("MEP could not be deleted on device - perhaps it " - + "does not exist. Continuing"); - mepCollection.remove(mep); - return false; - } - if (deleted) { - mepCollection.remove(mep); - return true; - } else { - return false; + Optional mepOptional = mepStore.getMep(key); + if (mepOptional.isPresent()) { + Mep mep = mepOptional.get(); + DeviceId mepDeviceId = mep.deviceId(); + if (deviceService.getDevice(mepDeviceId) == null) { + throw new CfmConfigException("Device not found " + mepDeviceId); + } else if (!deviceService.getDevice(mepDeviceId).is(CfmMepProgrammable.class)) { + throw new CfmConfigException("Device " + mepDeviceId + + " does not support CfmMepProgrammable behaviour."); + } + + log.debug("Retrieving MEP reults for Mep {} in MD {}, MA {} on Device {}", + mep.mepId(), mdName, maName, mepDeviceId); + + return deviceService.getDevice(mepDeviceId) + .as(CfmMepProgrammable.class).getMep(mdName, maName, mepId); + } else { + return null; + } + } + + @Override + public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId, + Optional oldMd) throws CfmConfigException { + MepKeyId key = new MepKeyId(mdName, maName, mepId); + + //Will throw IllegalArgumentException if ma does not exist + cfmMdService.getMaintenanceAssociation(mdName, maName); + + //Get the device ID from the MEP + Optional deletedMep = mepStore.getMep(key); + if (!deletedMep.isPresent()) { + log.warn("MEP {} not found when deleting Mep", key); + return false; + } + + DeviceId mepDeviceId = deletedMep.get().deviceId(); + boolean deleted = mepStore.deleteMep(key); + + Device mepDevice = deviceService.getDevice(mepDeviceId); + if (mepDevice == null || !mepDevice.is(CfmMepProgrammable.class)) { + throw new CfmConfigException("Unexpeced fault on device driver for " + + mepDeviceId); + } + try { + deleted = mepDevice.as(CfmMepProgrammable.class) + .deleteMep(mdName, maName, mepId, oldMd); + } catch (CfmConfigException e) { + log.warn("MEP could not be deleted on device - perhaps it " + + "does not exist. Continuing"); + } + + //Iterate through all other devices and remove as a Remote Mep + int mepsOnMdCount = 0; + int mepsOnMaCount = 0; + for (Mep mep : mepStore.getAllMeps()) { + if (mep.deviceId().equals(mepDeviceId) && mdName.equals(mep.mdId())) { + mepsOnMdCount++; + if (maName.equals(mep.maId())) { + mepsOnMaCount++; } } + List alreadyHandledDevices = new ArrayList<>(); + if (mep.deviceId().equals(mepDeviceId) || !mep.mdId().equals(mdName) || + !mep.maId().equals(maName) || + alreadyHandledDevices.contains(mep.deviceId())) { + continue; + } + deviceService.getDevice(mep.deviceId()) + .as(CfmMepProgrammable.class) + .deleteMaRemoteMepOnDevice(mdName, maName, mepId); + alreadyHandledDevices.add(mep.deviceId()); + log.info("Deleted RMep entry on {} on device {}", + mdName.mdName() + "/" + maName.maName(), mep.deviceId()); } - return false; + + //Also if this is the last MEP in this MA then delete this MA from device + //If this is the last MA in this MD on device, then delete the MD from the device + if (mepsOnMdCount == 0) { + boolean deletedMd = deviceService.getDevice(mepDeviceId) + .as(CfmMepProgrammable.class).deleteMdOnDevice(mdName, oldMd); + log.info("Deleted MD {} from Device {}", mdName.mdName(), mepDeviceId); + } else if (mepsOnMaCount == 0) { + boolean deletedMa = deviceService.getDevice(mepDeviceId) + .as(CfmMepProgrammable.class).deleteMaOnDevice(mdName, maName, oldMd); + log.info("Deleted MA {} from Device {}", + mdName.mdName() + "/" + maName.maName(), mepDeviceId); + } + + return deleted; } @Override public boolean createMep(MdId mdName, MaIdShort maName, Mep newMep) throws CfmConfigException { + MepKeyId key = new MepKeyId(mdName, maName, newMep.mepId()); log.debug("Creating MEP " + newMep.mepId() + " on MD {}, MA {} on Device {}", mdName, maName, newMep.deviceId().toString()); - for (Mep mep : mepCollection) { - if (mep.mdId().equals(mdName) && mep.maId().equals(maName) - && mep.mepId().equals(newMep.mepId())) { - return false; - } + if (mepStore.getMep(key).isPresent()) { + return false; } //Will throw IllegalArgumentException if ma does not exist @@ -225,7 +306,24 @@ public class CfmMepManager deviceService.getDevice(mepDeviceId).as(CfmMepProgrammable.class).createMep(mdName, maName, newMep); log.debug("MEP created on {}", mepDeviceId); if (deviceResult) { - return mepCollection.add(newMep); + boolean alreadyExisted = mepStore.createUpdateMep(key, newMep); + + //Add to other Remote Mep List on other devices + for (Mep mep:mepStore.getMepsByMdMa(mdName, maName)) { + List alreadyHandledDevices = new ArrayList<>(); + if (mep.deviceId().equals(mepDeviceId) || + alreadyHandledDevices.contains(mep.deviceId())) { + continue; + } + boolean created = deviceService.getDevice(mep.deviceId()) + .as(CfmMepProgrammable.class) + .createMaRemoteMepOnDevice(mdName, maName, newMep.mepId()); + alreadyHandledDevices.add(mep.deviceId()); + log.info("Created RMep entry on {} on device {}", + mdName.mdName() + "/" + maName.maName(), mep.deviceId()); + } + + return !alreadyExisted; } else { return deviceResult; } @@ -233,60 +331,253 @@ public class CfmMepManager @Override public void transmitLoopback(MdId mdName, MaIdShort maName, - MepId mepId, MepLbCreate lbCreate) throws CfmConfigException { - for (Mep mep : mepCollection) { - if (mep.mdId().equals(mdName) && mep.maId().equals(maName) - && mep.mepId().equals(mepId)) { - log.debug("Transmitting Loopback on MEP {}/{}/{} on Device {}", - mdName, maName, mepId, mep.deviceId()); - deviceService.getDevice(mep.deviceId()) - .as(CfmMepProgrammable.class) - .transmitLoopback(mdName, maName, mepId, lbCreate); - return; - } - } - throw new CfmConfigException("Mep " + mdName + "/" + maName + "/" - + mepId + " not found when calling Transmit Loopback"); + MepId mepId, MepLbCreate lbCreate) throws CfmConfigException { + MepKeyId key = new MepKeyId(mdName, maName, mepId); + Mep mep = mepStore.getMep(key) + .orElseThrow(() -> new CfmConfigException("Mep " + mdName + "/" + maName + "/" + + mepId + " not found when calling Transmit Loopback")); + + log.debug("Transmitting Loopback on MEP {} on Device {}", + key, mep.deviceId()); + deviceService.getDevice(mep.deviceId()) + .as(CfmMepProgrammable.class) + .transmitLoopback(mdName, maName, mepId, lbCreate); } @Override public void abortLoopback(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException { - for (Mep mep : mepCollection) { - if (mep.mdId().equals(mdName) && mep.maId().equals(maName) - && mep.mepId().equals(mepId)) { - log.debug("Aborting Loopback on MEP {}/{}/{} on Device {}", - mdName, maName, mepId, mep.deviceId()); - deviceService.getDevice(mep.deviceId()) - .as(CfmMepProgrammable.class) - .abortLoopback(mdName, maName, mepId); - return; - } - } - throw new CfmConfigException("Mep " + mdName + "/" + maName + "/" - + mepId + " not found when calling Transmit Loopback"); + + MepKeyId key = new MepKeyId(mdName, maName, mepId); + Mep mep = mepStore.getMep(key) + .orElseThrow(() -> new CfmConfigException("Mep " + mdName + "/" + maName + "/" + + mepId + " not found when calling Aborting Loopback")); + + log.debug("Aborting Loopback on MEP {} on Device {}", + key, mep.deviceId()); + deviceService.getDevice(mep.deviceId()) + .as(CfmMepProgrammable.class) + .abortLoopback(mdName, maName, mepId); } @Override public void transmitLinktrace(MdId mdName, MaIdShort maName, MepId mepId, - MepLtCreate ltCreate) { + MepLtCreate ltCreate) { throw new UnsupportedOperationException("Not yet implemented"); } - private class InternalDeviceListener implements DeviceListener { + private class InternalMdListener implements MdListener { @Override - public void event(DeviceEvent event) { + public boolean isRelevant(MdEvent event) { + return event.type().equals(MdEvent.Type.MD_REMOVED) || + event.type().equals(MdEvent.Type.MA_REMOVED); + } + + @Override + public void event(MdEvent event) { + MdId mdName = event.subject(); switch (event.type()) { - case DEVICE_REMOVED: - case DEVICE_AVAILABILITY_CHANGED: - DeviceId deviceId = event.subject().id(); - if (!deviceService.isAvailable(deviceId)) { - log.warn("Device {} has been removed or changed", deviceId); - } + case MA_REMOVED: + case MD_REMOVED: + log.trace("Event {} receieved from MD Service for {}", event.type(), mdName); + scheduleEventHandlerIfNotScheduled(event); break; default: + log.warn("Unhandled Event {} received from MD Service", event.type()); break; } } } + + private class InternalDeviceListener implements DeviceListener { + @Override + public boolean isRelevant(DeviceEvent event) { + return event.type().equals(DeviceEvent.Type.DEVICE_REMOVED); + } + + @Override + public void event(DeviceEvent event) { + DeviceId deviceId = event.subject().id(); + switch (event.type()) { + case DEVICE_ADDED: + case PORT_UPDATED: + case PORT_ADDED: + case DEVICE_UPDATED: + case DEVICE_REMOVED: + case DEVICE_AVAILABILITY_CHANGED: + log.trace("Event {} received from Device Service", event.type()); + scheduleEventHandlerIfNotScheduled(event); + break; + default: + log.warn("Unhandled Event {} received from Device Service", event.type()); + break; + } + } + } + + @SuppressWarnings("rawtypes") + private void scheduleEventHandlerIfNotScheduled(Event event) { + synchronized (THREAD_SCHED_LOCK) { + eventQueue.add(event); + numOfEventsQueued++; + + if ((numOfHandlerScheduled - numOfHandlerExecution) == 0) { + //No pending scheduled event handling threads. So start a new one. + eventHandlerFuture = executorService + .schedule(eventHandler, 100, TimeUnit.MILLISECONDS); + numOfHandlerScheduled++; + } + log.trace("numOfEventsQueued {}, numOfEventHandlerScheduled {}", + numOfEventsQueued, + numOfHandlerScheduled); + } + } + + private class InternalEventHandler implements Runnable { + @Override + public void run() { + try { + while (true) { + @SuppressWarnings("rawtypes") + Event event; + synchronized (THREAD_SCHED_LOCK) { + if (!eventQueue.isEmpty()) { + event = eventQueue.poll(); + numOfEventsExecuted++; + } else { + numOfHandlerExecution++; + log.debug("numOfHandlerExecution {} numOfEventsExecuted {}", + numOfHandlerExecution, numOfEventsExecuted); + break; + } + } + if (event.type() == DeviceEvent.Type.DEVICE_REMOVED) { + DeviceId deviceId = ((Device) event.subject()).id(); + log.info("Processing device removal event for unavailable device {}", + deviceId); + processDeviceRemoved((Device) event.subject()); + } else if (event.type() == MdEvent.Type.MD_REMOVED) { + MdId mdName = (MdId) event.subject(); + log.info("Processing MD removal event for MD {}", + mdName); + processMdRemoved(mdName, ((MdEvent) event).md().get()); + } else if (event.type() == MdEvent.Type.MA_REMOVED) { + MdId mdName = (MdId) event.subject(); + MaIdShort maName = ((MdEvent) event).maId().get(); + log.info("Processing MA removal event for MA {}", + mdName.mdName() + "/" + maName.maName()); + processMaRemoved(mdName, maName, ((MdEvent) event).md().get()); + } + } + } catch (Exception e) { + log.error("CfmMepService event handler " + + "thread thrown an exception: {}", e); + } + } + } + + /** + * This removes a MEP from the internal list of Meps, and updates remote meps list on other Meps. + * Note: This does not call the device's CfmMepProgrammable, because there + * would be no point as the device has already been removed from ONOS. + * The configuration for this MEP may still be present on the actual device, and + * any future config would have to be careful to wipe the Mep from the device + * before applying a Mep again + * @param removedDevice The device that has been removed + */ + protected void processDeviceRemoved(Device removedDevice) { + log.warn("Remove Mep(s) associated with Device: " + removedDevice.id()); + Collection mepListForDevice = mepStore.getMepsByDeviceId(removedDevice.id()); + + + for (Mep mep:mepStore.getAllMeps()) { + for (Mep mepForDevice:mepListForDevice) { + if (mep.mdId().equals(mepForDevice.mdId()) && mep.maId().equals(mepForDevice.maId())) { + Device mepDevice = deviceService.getDevice(mep.deviceId()); + log.info("Removing Remote Mep {} from MA{} on device {}", + mepForDevice.mepId(), + mep.mdId().mdName() + "/" + mep.maId().maName(), + mepDevice.id()); + try { + mepDevice.as(CfmMepProgrammable.class) + .deleteMaRemoteMepOnDevice(mep.mdId(), mep.maId(), mepForDevice.mepId()); + } catch (CfmConfigException e) { + log.error("Error when removing Remote Mep {} from MA {}. Continuing.", + mep.mdId().mdName() + "/" + mep.maId().maName(), + mepForDevice.mepId()); + } + } + } + } + + for (Iterator iter = mepListForDevice.iterator(); iter.hasNext();) { + mepStore.deleteMep(new MepKeyId(iter.next())); + } + } + + protected void processMaRemoved(MdId mdId, MaIdShort maId, MaintenanceDomain oldMd) { + Set deviceIdsRemoved = new HashSet<>(); + + for (Iterator iter = mepStore.getMepsByMdMa(mdId, maId).iterator(); iter.hasNext();) { + Mep mepForMdMa = iter.next(); + DeviceId mepDeviceId = mepForMdMa.deviceId(); + try { + deviceService.getDevice(mepDeviceId).as(CfmMepProgrammable.class) + .deleteMep(mdId, maId, mepForMdMa.mepId(), Optional.of(oldMd)); + deviceIdsRemoved.add(mepDeviceId); + } catch (CfmConfigException e) { + log.warn("Could not delete MEP {} from Device {}", mepForMdMa.mepId(), mepDeviceId, e); + } + iter.remove(); + + log.info("Removed MEP {} from Device {} because MA {} was deleted", + mepForMdMa.mepId(), mepDeviceId, mdId.mdName() + "/" + maId.maName()); + } + + deviceIdsRemoved.forEach(deviceId -> { + try { + deviceService.getDevice(deviceId).as(CfmMepProgrammable.class) + .deleteMaOnDevice(mdId, maId, Optional.of(oldMd)); + } catch (CfmConfigException e) { + log.warn("Could not delete MA {} from Device {}", + mdId.mdName() + "/" + maId.maName(), deviceId, e); + } + }); + } + + protected void processMdRemoved(MdId mdId, MaintenanceDomain oldMd) { + Set deviceIdsRemoved = new HashSet<>(); + for (Iterator iter = mepStore.getMepsByMd(mdId).iterator(); iter.hasNext();) { + Mep mep = iter.next(); + DeviceId mepDeviceId = mep.deviceId(); + try { + deviceService.getDevice(mepDeviceId).as(CfmMepProgrammable.class) + .deleteMep(mdId, mep.maId(), mep.mepId(), Optional.of(oldMd)); + deviceIdsRemoved.add(mepDeviceId); + } catch (CfmConfigException e) { + log.warn("Could not delete MEP {} from Device {}", mep.mepId(), mepDeviceId, e); + } + iter.remove(); + log.info("Removed MEP {} from Device {} because MD {} was deleted", + mep.mepId(), mepDeviceId, mdId.mdName()); + } + + deviceIdsRemoved.forEach(deviceId -> { + try { + deviceService.getDevice(deviceId).as(CfmMepProgrammable.class) + .deleteMdOnDevice(mdId, Optional.of(oldMd)); + } catch (CfmConfigException e) { + log.warn("Could not delete MD {} from Device {}", + mdId.mdName(), deviceId, e); + } + }); + } + + private class InternalStoreDelegate implements MepStoreDelegate { + @Override + public void notify(CfmMepEvent event) { + log.debug("New Mep event: {}", event); + eventDispatcher.post(event); + } + } } diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMdStore.java b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMdStore.java index 964a56a12d..82c550d19e 100644 --- a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMdStore.java +++ b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMdStore.java @@ -19,6 +19,7 @@ import com.google.common.net.InternetDomainName; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.felix.scr.annotations.Activate; import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; import org.apache.felix.scr.annotations.Reference; import org.apache.felix.scr.annotations.ReferenceCardinality; import org.apache.felix.scr.annotations.Service; @@ -42,6 +43,7 @@ import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; import org.onosproject.incubator.net.l2monitoring.cfm.service.MdEvent; import org.onosproject.incubator.net.l2monitoring.cfm.service.MdStore; import org.onosproject.incubator.net.l2monitoring.cfm.service.MdStoreDelegate; @@ -58,6 +60,8 @@ import org.slf4j.LoggerFactory; import java.util.Collection; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; /** * Maintenance Domain Store implementation backed by consistent map. @@ -75,7 +79,7 @@ public class DistributedMdStore extends AbstractStore private ConsistentMap maintenanceDomainConsistentMap; private Map maintenanceDomainMap; - private final InternalMdListener listener = new InternalMdListener(); + private MapEventListener mapListener = null; @Activate public void activate() { @@ -112,8 +116,17 @@ public class DistributedMdStore extends AbstractStore .cfm.Component.TagType.class) .build("md"))) .build(); + mapListener = new InternalMdListener(); + maintenanceDomainConsistentMap.addListener(mapListener); maintenanceDomainMap = maintenanceDomainConsistentMap.asJavaMap(); + log.info("MDStore started"); + } + + @Deactivate + public void deactivate() { + maintenanceDomainConsistentMap.removeListener(mapListener); + log.info("Stopped"); } @Override @@ -141,19 +154,65 @@ public class DistributedMdStore extends AbstractStore @Override public void event(MapEvent mapEvent) { final MdEvent.Type type; + MaIdShort maId = null; switch (mapEvent.type()) { case INSERT: type = MdEvent.Type.MD_ADDED; break; case UPDATE: - type = MdEvent.Type.MD_UPDATED; + // Examine the diff to see if it was a removal or addition of an MA caused it + if (mapEvent.oldValue().value().maintenanceAssociationList().size() > + mapEvent.newValue().value().maintenanceAssociationList().size()) { + Set newMaIds = mapEvent.newValue().value().maintenanceAssociationList() + .stream() + .map(MaintenanceAssociation::maId) + .collect(Collectors.toSet()); + Optional removedMa = + mapEvent.oldValue().value().maintenanceAssociationList() + .stream() + .filter(maOld -> !newMaIds.contains(maOld.maId())).findFirst(); + if (removedMa.isPresent()) { + maId = removedMa.get().maId(); + } + type = MdEvent.Type.MA_REMOVED; + } else if (mapEvent.oldValue().value().maintenanceAssociationList().size() < + mapEvent.newValue().value().maintenanceAssociationList().size()) { + Set oldMaIds = mapEvent.oldValue().value().maintenanceAssociationList() + .stream() + .map(MaintenanceAssociation::maId) + .collect(Collectors.toSet()); + Optional addedMa = + mapEvent.newValue().value().maintenanceAssociationList() + .stream() + .filter(maNew -> !oldMaIds.contains(maNew.maId())).findFirst(); + if (addedMa.isPresent()) { + maId = addedMa.get().maId(); + } + type = MdEvent.Type.MA_ADDED; + } else { + type = MdEvent.Type.MD_UPDATED; + } break; case REMOVE: default: type = MdEvent.Type.MD_REMOVED; break; } - notifyDelegate(new MdEvent(type, mapEvent.key())); + if (mapEvent.oldValue() != null && mapEvent.oldValue().value() != null) { + MaintenanceDomain oldMd = mapEvent.oldValue().value(); + try { + if (maId != null) { + notifyDelegate(new MdEvent(type, mapEvent.key(), oldMd, maId)); + } else { + notifyDelegate(new MdEvent(type, mapEvent.key(), oldMd)); + } + } catch (CfmConfigException e) { + log.warn("Unable to copy MD {}", oldMd); + notifyDelegate(new MdEvent(type, mapEvent.key())); + } + } else { + notifyDelegate(new MdEvent(type, mapEvent.key())); + } } } } diff --git a/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMepStore.java b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMepStore.java new file mode 100644 index 0000000000..c21166aee1 --- /dev/null +++ b/incubator/net/src/main/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/DistributedMepStore.java @@ -0,0 +1,193 @@ +/* + * Copyright 2017-present Open Networking Foundation + * + * 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.onosproject.incubator.net.l2monitoring.cfm.impl; + +import org.apache.felix.scr.annotations.Activate; +import org.apache.felix.scr.annotations.Component; +import org.apache.felix.scr.annotations.Deactivate; +import org.apache.felix.scr.annotations.Reference; +import org.apache.felix.scr.annotations.ReferenceCardinality; +import org.apache.felix.scr.annotations.Service; +import org.onlab.packet.IpAddress; +import org.onlab.packet.VlanId; +import org.onlab.util.KryoNamespace; +import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep; +import org.onosproject.incubator.net.l2monitoring.cfm.Mep; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaId2Octet; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdIccY1731; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdPrimaryVid; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdRfc2685VpnId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdDomainName; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdMacUint; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdNone; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepEvent; +import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStore; +import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStoreDelegate; +import org.onosproject.net.DeviceId; +import org.onosproject.net.NetworkResource; +import org.onosproject.net.PortNumber; +import org.onosproject.store.AbstractStore; +import org.onosproject.store.serializers.KryoNamespaces; +import org.onosproject.store.service.ConsistentMap; +import org.onosproject.store.service.MapEvent; +import org.onosproject.store.service.MapEventListener; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.time.Duration; +import java.util.Collection; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * MEP Store implementation backed by consistent map. + */ +@Component(immediate = true) +@Service +public class DistributedMepStore extends AbstractStore + implements MepStore { + + private final Logger log = LoggerFactory.getLogger(getClass()); + + @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY) + protected StorageService storageService; + + private ConsistentMap mepConsistentMap; + private Map mepMap; + + private MapEventListener mapListener = null; + + @Activate + public void activate() { + mepConsistentMap = storageService + .consistentMapBuilder() + .withName("onos-cfm-mep-map") + .withSerializer(Serializer.using(new KryoNamespace.Builder() + .register(KryoNamespaces.API) + .register(DefaultMep.class) + .register(MepId.class) + .register(MepKeyId.class) + .register(NetworkResource.class) + .register(DeviceId.class) + .register(PortNumber.class) + .register(Mep.MepDirection.class) + .register(VlanId.class) + .register(Mep.Priority.class) + .register(Mep.FngAddress.class) + .register(Mep.FngAddressType.class) + .register(IpAddress.class) + .register(Mep.LowestFaultDefect.class) + .register(Duration.class) + .register(MdIdCharStr.class) + .register(MdIdDomainName.class) + .register(MdIdMacUint.class) + .register(MdIdNone.class) + .register(MaIdCharStr.class) + .register(MaIdShort.class) + .register(MaId2Octet.class) + .register(MaIdIccY1731.class) + .register(MaIdPrimaryVid.class) + .register(MaIdRfc2685VpnId.class) + .build("mep"))) + .build(); + mapListener = new InternalMepListener(); + mepConsistentMap.addListener(mapListener); + + mepMap = mepConsistentMap.asJavaMap(); + log.info("MepStore started"); + } + + @Deactivate + public void deactivate() { + mepConsistentMap.removeListener(mapListener); + log.info("MepStore stopped"); + } + + @Override + public Collection getAllMeps() { + return mepMap.values(); + } + + @Override + public Collection getMepsByMd(MdId mdName) { + return mepMap.values().stream() + .filter(mep -> mep.mdId().equals(mdName)) + .collect(Collectors.toList()); + } + + @Override + public Collection getMepsByMdMa(MdId mdName, MaIdShort maName) { + return mepMap.values().stream() + .filter(mep -> mep.mdId().equals(mdName) && mep.maId().equals(maName)) + .collect(Collectors.toList()); + } + + @Override + public Collection getMepsByDeviceId(DeviceId deviceId) { + return mepMap.values().stream() + .filter(mep -> mep.deviceId().equals(deviceId)) + .collect(Collectors.toList()); + } + + @Override + public Optional getMep(MepKeyId mepKeyId) { + return mepMap.values().stream() + .filter(mep -> mep.mdId().equals(mepKeyId.mdId()) && + mep.maId().equals(mepKeyId.maId()) && + mep.mepId().equals(mepKeyId.mepId())) + .findFirst(); + } + + @Override + public boolean deleteMep(MepKeyId mepKeyId) { + return mepMap.remove(mepKeyId) == null ? false : true; + } + + @Override + public boolean createUpdateMep(MepKeyId mepKeyId, Mep mep) { + return mepMap.put(mepKeyId, mep) == null ? false : true; + } + + private class InternalMepListener implements MapEventListener { + + @Override + public void event(MapEvent mapEvent) { + final CfmMepEvent.Type type; + + switch (mapEvent.type()) { + case INSERT: + type = CfmMepEvent.Type.MEP_ADDED; + break; + case UPDATE: + type = CfmMepEvent.Type.MEP_UPDATED; + break; + default: + case REMOVE: + type = CfmMepEvent.Type.MEP_REMOVED; + } + notifyDelegate(new CfmMepEvent(type, mapEvent.key())); + } + } +} diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManagerTest.java index a0de513ff8..fa79546341 100644 --- a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManagerTest.java +++ b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMdManagerTest.java @@ -36,10 +36,13 @@ import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMaintenanceDomain; import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceAssociation; import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdCharStr; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService; +import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; import org.onosproject.mastership.MastershipServiceAdapter; import org.onosproject.net.DeviceId; import org.onosproject.store.service.TestStorageService; @@ -50,6 +53,7 @@ import java.util.Set; import java.util.concurrent.atomic.AtomicLong; import static junit.framework.TestCase.assertFalse; +import static org.easymock.EasyMock.createMock; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @@ -61,6 +65,11 @@ import static org.onosproject.net.NetTestTools.injectEventDispatcher; public class CfmMdManagerTest { private static final NodeId NID_LOCAL = new NodeId("local"); private static final IpAddress LOCALHOST = IpAddress.valueOf("127.0.0.1"); + private static final MaIdShort MA_ID_1_1 = MaIdCharStr.asMaId("test-ma-1-1"); + private static final MaIdShort MA_ID_1_2 = MaIdCharStr.asMaId("test-ma-1-2"); + private static final MdId MD_ID_1 = MdIdCharStr.asMdId("test-md-1"); + + private final CfmMepService mepService = createMock(CfmMepService.class); private DistributedMdStore mdStore; private CfmMdService service; @@ -71,7 +80,7 @@ public class CfmMdManagerTest { mdStore = new DistributedMdStore(); MaintenanceAssociation maTest11 = DefaultMaintenanceAssociation - .builder(MaIdCharStr.asMaId("test-ma-1-1"), 9) + .builder(MA_ID_1_1, MD_ID_1.getNameLength()) .ccmInterval(MaintenanceAssociation.CcmInterval.INTERVAL_10MIN) .maNumericId((short) 1) .addToRemoteMepIdList(MepId.valueOf((short) 101)) @@ -83,7 +92,7 @@ public class CfmMdManagerTest { .build(); MaintenanceAssociation maTest12 = DefaultMaintenanceAssociation - .builder(MaIdCharStr.asMaId("test-ma-1-2"), 9) + .builder(MA_ID_1_2, MD_ID_1.getNameLength()) .ccmInterval(MaintenanceAssociation.CcmInterval.INTERVAL_10MIN) .maNumericId((short) 2) .addToRemoteMepIdList(MepId.valueOf((short) 201)) @@ -95,7 +104,7 @@ public class CfmMdManagerTest { .build(); MaintenanceDomain mdTest1 = DefaultMaintenanceDomain - .builder(MdIdCharStr.asMdId("test-md-1")) + .builder(MD_ID_1) .mdLevel(MaintenanceDomain.MdLevel.LEVEL1) .mdNumericId((short) 1) .addToMaList(maTest11) @@ -105,6 +114,7 @@ public class CfmMdManagerTest { TestUtils.setField(mdStore, "storageService", new TestStorageService()); TestUtils.setField(mdStore, "clusterService", new CfmMdManagerTest.TestClusterService()); TestUtils.setField(mdStore, "mastershipService", new CfmMdManagerTest.TestMastershipService()); + mdStore.activate(); mdStore.createUpdateMaintenanceDomain(mdTest1); @@ -113,6 +123,7 @@ public class CfmMdManagerTest { service = manager; TestUtils.setField(manager, "storageService", new TestStorageService()); TestUtils.setField(manager, "coreService", new TestCoreService()); + TestUtils.setField(manager, "mepService", mepService); injectEventDispatcher(manager, new TestEventDispatcher()); manager.appId = new CfmMdManagerTest.TestApplicationId(0, "CfmMdManagerTest"); @@ -339,7 +350,6 @@ public class CfmMdManagerTest { MdIdCharStr.asMdId("test-md-1")).size()); } - public class TestApplicationId extends DefaultApplicationId { public TestApplicationId(int id, String name) { super(id, name); diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManagerTest.java b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManagerTest.java index a82585872b..5941f6f508 100644 --- a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManagerTest.java +++ b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/CfmMepManagerTest.java @@ -37,10 +37,12 @@ import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MaIdShort; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdId; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MdIdCharStr; import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepId; +import org.onosproject.incubator.net.l2monitoring.cfm.identifier.MepKeyId; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmConfigException; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMdService; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepProgrammable; import org.onosproject.incubator.net.l2monitoring.cfm.service.CfmMepService; +import org.onosproject.incubator.net.l2monitoring.cfm.service.MepStore; import org.onosproject.incubator.net.l2monitoring.soam.SoamDmProgrammable; import org.onosproject.incubator.net.l2monitoring.soam.impl.TestSoamDmProgrammable; import org.onosproject.net.AbstractProjectableModel; @@ -57,21 +59,30 @@ import org.onosproject.net.driver.DefaultDriver; import org.onosproject.net.driver.Driver; import org.onosproject.net.driver.DriverService; import org.onosproject.net.provider.ProviderId; +import org.onosproject.store.service.AsyncDocumentTree; +import org.onosproject.store.service.DocumentTreeBuilder; +import org.onosproject.store.service.Serializer; +import org.onosproject.store.service.StorageService; +import org.onosproject.store.service.TestAsyncDocumentTree; +import org.onosproject.store.service.TestStorageService; +import org.onosproject.store.service.TestTopic; +import org.onosproject.store.service.Topic; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.Optional; import java.util.concurrent.atomic.AtomicLong; -import static junit.framework.TestCase.fail; import static org.easymock.EasyMock.createMock; import static org.easymock.EasyMock.expect; import static org.easymock.EasyMock.replay; -import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; +import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.fail; import static org.onosproject.net.NetTestTools.injectEventDispatcher; /** @@ -91,18 +102,32 @@ public class CfmMepManagerTest { private CfmMepService mepService; private CfmMepManager mepManager; + private MepStore mepStore; + private StorageService storageService; protected static final MdId MDNAME1 = MdIdCharStr.asMdId("md-1"); + protected static final MdId MDNAME2 = MdIdCharStr.asMdId("md-2"); protected static final MaIdShort MANAME1 = MaIdCharStr.asMaId("ma-1-1"); + protected static final MaIdShort MANAME2 = MaIdCharStr.asMaId("ma-2-2"); private MaintenanceAssociation ma1; + private MaintenanceAssociation ma2; protected static final MepId MEPID1 = MepId.valueOf((short) 10); + protected static final MepId MEPID11 = MepId.valueOf((short) 11); + protected static final MepId MEPID12 = MepId.valueOf((short) 12); protected static final MepId MEPID2 = MepId.valueOf((short) 20); + protected static final MepId MEPID21 = MepId.valueOf((short) 21); + protected static final MepId MEPID22 = MepId.valueOf((short) 22); + protected static final DeviceId DEVICE_ID1 = DeviceId.deviceId("netconf:1.2.3.4:830"); protected static final DeviceId DEVICE_ID2 = DeviceId.deviceId("netconf:2.2.3.4:830"); private Mep mep1; + private Mep mep11; + private Mep mep12; private Mep mep2; + private Mep mep21; + private Mep mep22; private Device device1; private Device device2; @@ -112,12 +137,19 @@ public class CfmMepManagerTest { @Before public void setup() throws CfmConfigException { mepManager = new CfmMepManager(); + mepStore = new DistributedMepStore(); + storageService = new MockStorageService(); ma1 = DefaultMaintenanceAssociation.builder(MANAME1, MDNAME1.getNameLength()).build(); + ma2 = DefaultMaintenanceAssociation.builder(MANAME2, MDNAME2.getNameLength()).build(); + + TestUtils.setField(mepStore, "storageService", storageService); + ((DistributedMepStore) mepStore).activate(); TestUtils.setField(mepManager, "coreService", new TestCoreService()); TestUtils.setField(mepManager, "deviceService", deviceService); TestUtils.setField(mepManager, "cfmMdService", mdService); + TestUtils.setField(mepManager, "mepStore", mepStore); injectEventDispatcher(mepManager, new TestEventDispatcher()); mepService = mepManager; @@ -125,12 +157,27 @@ public class CfmMepManagerTest { mep1 = DefaultMep.builder(MEPID1, DEVICE_ID1, PortNumber.P0, Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build(); + mepStore.createUpdateMep(new MepKeyId(MDNAME1, MANAME1, MEPID1), mep1); + + mep11 = DefaultMep.builder(MEPID11, DEVICE_ID1, PortNumber.P0, + Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build(); + mepStore.createUpdateMep(new MepKeyId(MDNAME1, MANAME1, MEPID11), mep11); + + mep12 = DefaultMep.builder(MEPID12, DEVICE_ID1, PortNumber.P0, + Mep.MepDirection.UP_MEP, MDNAME2, MANAME2).build(); + mepStore.createUpdateMep(new MepKeyId(MDNAME2, MANAME2, MEPID12), mep12); + mep2 = DefaultMep.builder(MEPID2, DEVICE_ID2, PortNumber.portNumber(2), Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build(); - List mepList = new ArrayList<>(); - mepList.add(mep1); - mepList.add(mep2); - TestUtils.setField(mepManager, "mepCollection", mepList); + mepStore.createUpdateMep(new MepKeyId(MDNAME1, MANAME1, MEPID2), mep2); + + mep21 = DefaultMep.builder(MEPID21, DEVICE_ID2, PortNumber.portNumber(2), + Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build(); + mepStore.createUpdateMep(new MepKeyId(MDNAME1, MANAME1, MEPID21), mep21); + + mep22 = DefaultMep.builder(MEPID22, DEVICE_ID2, PortNumber.portNumber(2), + Mep.MepDirection.UP_MEP, MDNAME2, MANAME2).build(); + mepStore.createUpdateMep(new MepKeyId(MDNAME2, MANAME2, MEPID22), mep22); device1 = new DefaultDevice( ProviderId.NONE, DEVICE_ID1, Device.Type.SWITCH, @@ -180,7 +227,7 @@ public class CfmMepManagerTest { Collection mepEntries = mepManager.getAllMeps(MDNAME1, MANAME1); - assertEquals(2, mepEntries.size()); + assertEquals(4, mepEntries.size()); } @Test @@ -232,12 +279,13 @@ public class CfmMepManagerTest { replay(mdService); expect(deviceService.getDevice(DEVICE_ID1)).andReturn(device1).anyTimes(); + expect(deviceService.getDevice(DEVICE_ID2)).andReturn(device2).anyTimes(); replay(deviceService); expect(driverService.getDriver(TEST_DRIVER)).andReturn(testDriver).anyTimes(); replay(driverService); - assertTrue(mepManager.deleteMep(MDNAME1, MANAME1, MEPID1)); + assertTrue(mepManager.deleteMep(MDNAME1, MANAME1, MEPID1, Optional.empty())); } @Test @@ -248,6 +296,7 @@ public class CfmMepManagerTest { replay(mdService); expect(deviceService.getDevice(DEVICE_ID1)).andReturn(device1).anyTimes(); + expect(deviceService.getDevice(DEVICE_ID2)).andReturn(device2).anyTimes(); replay(deviceService); expect(driverService.getDriver(TEST_DRIVER)).andReturn(testDriver).anyTimes(); @@ -257,6 +306,7 @@ public class CfmMepManagerTest { Mep mep3 = DefaultMep.builder(mepId3, DEVICE_ID1, PortNumber.portNumber(1), Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build(); + //Expecting false - since it was not found assertTrue(mepManager.createMep(MDNAME1, MANAME1, mep3)); } @@ -364,6 +414,56 @@ public class CfmMepManagerTest { } } + @Test + public void testDeviceRemoved() throws CfmConfigException { + expect(mdService.getMaintenanceAssociation(MDNAME1, MANAME1)) + .andReturn(Optional.ofNullable(ma1)) + .anyTimes(); + expect(mdService.getMaintenanceAssociation(MDNAME2, MANAME2)) + .andReturn(Optional.ofNullable(ma2)) + .anyTimes(); + replay(mdService); + + expect(deviceService.getDevice(DEVICE_ID1)).andReturn(device1).anyTimes(); + expect(deviceService.getDevice(DEVICE_ID2)).andReturn(device2).anyTimes(); + replay(deviceService); + + expect(driverService.getDriver(TEST_DRIVER)).andReturn(testDriver).anyTimes(); + replay(driverService); + +// This is arranged like +// device1 device2 +// / \ / \ +// md-1 md-2 md-1 md-2 +// | | | | +// ma-1-1 ma-2-2 ma-1-1 ma-2-2 +// / \ | / \ \ +// mep1 mep11 mep12 mep2 mep21 mep22 + assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID1)); + assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID11)); + assertNotNull(mepService.getMep(MDNAME2, MANAME2, MEPID12)); + assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID2)); + assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID21)); + assertNotNull(mepService.getMep(MDNAME2, MANAME2, MEPID22)); + + //By deleting Device2 we expect Mep2,21,22 to have been deleted but Mep1,11,12 to remain + ((CfmMepManager) mepService).processDeviceRemoved(device2); + + assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID1)); + assertNotNull(mepService.getMep(MDNAME1, MANAME1, MEPID11)); + assertNotNull(mepService.getMep(MDNAME2, MANAME2, MEPID12)); + //The device 2 related ones are gone + assertNull(mepService.getMep(MDNAME1, MANAME1, MEPID2)); + assertNull(mepService.getMep(MDNAME1, MANAME1, MEPID21)); + assertNull(mepService.getMep(MDNAME2, MANAME2, MEPID22)); + + //Now delete device1 + ((CfmMepManager) mepService).processDeviceRemoved(device1); + assertNull(mepService.getMep(MDNAME1, MANAME1, MEPID1)); + assertNull(mepService.getMep(MDNAME1, MANAME1, MEPID11)); + assertNull(mepService.getMep(MDNAME2, MANAME2, MEPID12)); + } + private class TestCoreService extends CoreServiceAdapter { @Override @@ -378,4 +478,27 @@ public class CfmMepManagerTest { }; } } + + private static class MockStorageService extends TestStorageService { + @Override + public DocumentTreeBuilder documentTreeBuilder() { + return new DocumentTreeBuilder() { + @Override + public AsyncDocumentTree buildDocumentTree() { + return build(); + } + + @Override + @SuppressWarnings("unchecked") + public AsyncDocumentTree build() { + return new TestAsyncDocumentTree<>(name()); + } + }; + } + + @Override + public Topic getTopic(String name, Serializer serializer) { + return new TestTopic<>(name); + } + } } diff --git a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/TestCfmMepProgrammable.java b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/TestCfmMepProgrammable.java index 8d0f7659bd..3f4be0eb5a 100644 --- a/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/TestCfmMepProgrammable.java +++ b/incubator/net/src/test/java/org/onosproject/incubator/net/l2monitoring/cfm/impl/TestCfmMepProgrammable.java @@ -17,6 +17,7 @@ package org.onosproject.incubator.net.l2monitoring.cfm.impl; import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMep; import org.onosproject.incubator.net.l2monitoring.cfm.DefaultMepEntry; +import org.onosproject.incubator.net.l2monitoring.cfm.MaintenanceDomain; import org.onosproject.incubator.net.l2monitoring.cfm.Mep; import org.onosproject.incubator.net.l2monitoring.cfm.MepEntry; import org.onosproject.incubator.net.l2monitoring.cfm.MepLbCreate; @@ -30,8 +31,8 @@ import org.onosproject.net.PortNumber; import org.onosproject.net.driver.AbstractHandlerBehaviour; import java.util.ArrayList; -import java.util.Collection; import java.util.List; +import java.util.Optional; import static org.onosproject.incubator.net.l2monitoring.cfm.impl.CfmMepManagerTest.*; @@ -47,13 +48,16 @@ public class TestCfmMepProgrammable extends AbstractHandlerBehaviour implements deviceMepList.add(DefaultMep.builder(MEPID1, DEVICE_ID1, PortNumber.P0, Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build()); + deviceMepList.add(DefaultMep.builder(MEPID11, DEVICE_ID1, PortNumber.P0, + Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build()); + deviceMepList.add(DefaultMep.builder(MEPID12, DEVICE_ID1, PortNumber.P0, + Mep.MepDirection.UP_MEP, MDNAME2, MANAME2).build()); deviceMepList.add(DefaultMep.builder(MEPID2, DEVICE_ID2, PortNumber.portNumber(2), Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build()); - } - - @Override - public Collection getAllMeps(MdId mdName, MaIdShort maName) throws CfmConfigException { - return null; + deviceMepList.add(DefaultMep.builder(MEPID21, DEVICE_ID2, PortNumber.portNumber(2), + Mep.MepDirection.UP_MEP, MDNAME1, MANAME1).build()); + deviceMepList.add(DefaultMep.builder(MEPID22, DEVICE_ID2, PortNumber.portNumber(2), + Mep.MepDirection.UP_MEP, MDNAME2, MANAME2).build()); } @Override @@ -67,7 +71,8 @@ public class TestCfmMepProgrammable extends AbstractHandlerBehaviour implements } @Override - public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId) throws CfmConfigException { + public boolean deleteMep(MdId mdName, MaIdShort maName, MepId mepId, + Optional oldMd) throws CfmConfigException { return true; } @@ -76,6 +81,37 @@ public class TestCfmMepProgrammable extends AbstractHandlerBehaviour implements return true; } + @Override + public boolean deleteMdOnDevice(MdId mdId, Optional oldMd) throws CfmConfigException { + return false; + } + + @Override + public boolean deleteMaOnDevice(MdId mdId, MaIdShort maId, + Optional oldMd) throws CfmConfigException { + return false; + } + + @Override + public boolean createMdOnDevice(MdId mdId) throws CfmConfigException { + return false; + } + + @Override + public boolean createMaOnDevice(MdId mdId, MaIdShort maId) throws CfmConfigException { + return false; + } + + @Override + public boolean createMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException { + return false; + } + + @Override + public boolean deleteMaRemoteMepOnDevice(MdId mdId, MaIdShort maId, MepId remoteMep) throws CfmConfigException { + return false; + } + @Override public void transmitLoopback(MdId mdName, MaIdShort maName, MepId mepId, MepLbCreate lbCreate) throws CfmConfigException {