<groupId>org.opendaylight.packetcable</groupId>
<artifactId>packetcable</artifactId>
<version>1.3.0-SNAPSHOT</version>
+ <relativePath>..</relativePath>
</parent>
<artifactId>packetcable-driver</artifactId>
<packaging>bundle</packaging>
<groupId>org.opendaylight.mdsal</groupId>
<artifactId>yang-binding</artifactId>
</dependency>
+ <dependency>
+ <groupId>org.opendaylight.mdsal.model</groupId>
+ <artifactId>yang-ext</artifactId>
+ </dependency>
<dependency>
<groupId>org.opendaylight.yangtools</groupId>
<artifactId>yang-common</artifactId>
namespace "urn:packetcable";
prefix "pcmm";
- import ietf-yang-types { prefix yang; }
- import ietf-inet-types { prefix inet; }
+ import ietf-yang-types { prefix yang; revision-date "2010-09-24"; }
+ import ietf-inet-types { prefix inet; revision-date "2010-09-24"; }
+ import yang-ext { prefix ext; revision-date "2013-07-09"; }
description "This module contains the PCMM Converged Cable Access Platform (CCAP) definitions";
organization "OpenDaylight Project";
-
+
revision 2015-10-26 {
description "Corrected pluralization of containers/lists and added containers around lists where needed";
}
description "TOS/TC byte or mask";
}
+ identity ccap-context {
+ description "Identity used to mark ccap context";
+ }
+
+ identity app-context {
+ description "Identity used to mark app context";
+ }
// CCAP devices
container ccaps {
and a list of available Service Class Names.
";
key "ccapId";
+ ext:context-instance "ccap-context";
leaf ccapId {
type string;
- description "CCAP Identity";
- }
+ description "CCAP Identity";
+ mandatory true;
+ }
uses ccap-attributes;
}
}
+ grouping ccap-connection {
+ leaf ipAddress {
+ type inet:ip-address;
+ description "IP Address of CCAP";
+ mandatory true;
+ }
+ leaf port {
+ type inet:port-number;
+ description "COPS session TCP port number";
+ default 3918;
+ }
+ leaf connected {
+ config false;
+ type boolean;
+ description "COPS session state";
+ mandatory true;
+ }
+ leaf-list error {
+ config false;
+ type string;
+ description "Operational errors";
+ }
+// leaf idle-detect {
+// type uint8;
+// description "COPS connection idle timer (seconds)";
+// mandatory true;
+// }
+// leaf isIdle {
+// config false;
+// type boolean;
+// description "COPS connection idle state";
+// mandatory true;
+// }
+
+ leaf timestamp {
+ config false;
+ type yang:date-and-time;
+ description "Last update timestamp";
+ mandatory true;
+ }
+ }
+
grouping ccap-attributes {
description "
Each CCAP device has a COPS connection address:port,
a list of available Service Class Names.
";
container connection {
- leaf ipAddress {
- type inet:ip-address;
- description "IP Address of CCAP";
- }
- leaf port {
- type inet:port-number;
- description "COPS session TCP port number";
- default 3918;
- }
+ uses ccap-connection;
}
container amId {
leaf am-tag {
type uint16;
description "Application Manager Tag -- unique for this operator";
+ mandatory true;
}
leaf am-type {
type uint16;
description "Application Manager Type -- unique for this AM tag";
+ mandatory true;
}
}
leaf-list subscriber-subnets {
leaf-list downstream-scns {
type service-class-name;
}
+ leaf-list error {
+ config false;
+ type string;
+ description "ccap data errors";
+ }
}
// PCMM QoS Gates
container apps {
list app {
key "appId";
+ ext:context-instance "app-context";
leaf appId {
type string;
description "Application Identity";
key "subscriberId";
leaf subscriberId {
type string;
- description "Subscriber Identity -- must be a CM or CPE IP address";
+ description "Subscriber Identity -- must be a CM or CPE IP address";
+ mandatory true;
}
container gates {
list gate {
leaf gateId {
type string;
description "Qos Gate Identity";
+ mandatory true;
}
+ uses gate-operational-attributes;
uses pcmm-qos-gate-attributes;
}
}
}
}
+ grouping gate-operational-attributes {
+ leaf gatePath {
+ config false;
+ type string;
+ description "FQ Gate path app/subscriber/gate";
+ mandatory true;
+ }
+ leaf ccapId {
+ config false;
+ type string;
+ description "CCAP Identity";
+ mandatory true;
+ }
+ leaf cops-state {
+ config false;
+ type string;
+ description "Gate operational COPS state";
+ mandatory true;
+ }
+ leaf cops-gateId {
+ config false;
+ type string;
+ description "Gate operational COPS Id";
+ mandatory true;
+ }
+ leaf-list error {
+ config false;
+ type string;
+ description "Gate operational error";
+ }
+ leaf timestamp {
+ config false;
+ type yang:date-and-time;
+ description "Gate operational attributes timestamp";
+ mandatory true;
+ }
+ }
+
grouping pcmm-qos-gate-attributes {
uses pcmm-qos-gate-spec;
uses pcmm-qos-traffic-profile;
leaf service-class-name {
type service-class-name;
description "The Service Class Name (SCN). This SCN must be pre-provisioned on the target CCAP";
+ mandatory true;
}
}
}
leaf srcPort-start {
type inet:port-number;
description "TCP/UDP source port range start.";
+ mandatory true;
}
leaf srcPort-end {
type inet:port-number;
description "TCP/UDP source port range end.";
+ mandatory true;
}
leaf dstPort-start {
type inet:port-number;
description "TCP/UDP destination port range start.";
+ mandatory true;
}
leaf dstPort-end {
type inet:port-number;
description "TCP/UDP destination port range end.";
+ mandatory true;
}
}
leaf srcIp {
type inet:ipv4-address;
description "Source IPv4 address (exact match)";
+ mandatory true;
}
leaf dstIp {
type inet:ipv4-address;
description "Destination IPv4 address (exact match)";
+ mandatory true;
}
leaf tos-byte {
type tos-byte;
description "TOS/DSCP match";
+ mandatory true;
}
leaf tos-mask {
type tos-byte;
description "TOS/DSCP mask";
+ mandatory true;
}
leaf protocol {
type tp-protocol;
description "IPv4 transport protocol";
+ mandatory true;
}
leaf srcPort {
type inet:port-number;
description "TCP/UDP source port (exact match).";
+ mandatory true;
}
leaf dstPort {
type inet:port-number;
description "TCP/UDP destination port (exact match).";
+ mandatory true;
}
}
}
leaf srcIp {
type inet:ipv4-address;
description "Source IPv4 address match";
+ mandatory true;
}
leaf srcIpMask {
type inet:ipv4-address;
description "Source IPv4 mask";
+ mandatory true;
}
leaf dstIp {
type inet:ipv4-address;
description "Destination IPv4 address match";
+ mandatory true;
}
leaf dstIpMask {
type inet:ipv4-address;
description "Destination IPv4 mask";
+ mandatory true;
}
leaf tos-byte {
type tos-byte;
description "TOS/DSCP match";
+ mandatory true;
}
leaf tos-mask {
type tos-byte;
description "TOS/DSCP mask";
+ mandatory true;
}
leaf protocol {
type tp-protocol;
description "IPv4 transport protocol";
+ mandatory true;
}
uses tp-port-match-ranges;
}
container ipv6-classifier {
leaf srcIp6 {
type inet:ipv6-prefix;
- description "Source IPv6 prefix match in <address/len> notation";
+ description "Source IPv6 prefix match in 'address/len' notation";
+ mandatory true;
}
leaf dstIp6 {
type inet:ipv6-prefix;
- description "Destination IPv6 prefix match in <address/len> notation";
+ description "Destination IPv6 prefix match in 'address/len' notation";
+ mandatory true;
}
leaf tc-low {
type tos-byte;
description "TC low range match";
+ mandatory true;
}
leaf tc-high {
type tos-byte;
description "TC high range match";
+ mandatory true;
}
leaf tc-mask {
type tos-byte;
description "TC mask";
+ mandatory true;
}
leaf next-hdr {
type tp-protocol;
description "IPv6 Next Header";
+ mandatory true;
}
leaf flow-label {
type uint32 {
range "0 .. 1048575";
}
description "IPv6 Flow Label (20 bits)";
+ mandatory true;
}
uses tp-port-match-ranges;
}
"name": "CCAP/CMTS",
"description": "Sample PUT, GET, & DELETE of CMTS into ODL",
"order": [
+ "1f68f1dc-1d2b-8d04-b7be-0e6fc03fd90a",
"44ba6dfe-ca0c-376a-0322-b3db2af6eb2f",
"0c1e6f25-e33b-e3ed-fc59-02f2b569a9ed",
"a817933b-9398-23d1-b269-68f81ba51bc7",
"f403ebfd-d7d7-c94e-0ee5-3bc864098250",
"672adfee-68a5-5281-d638-72c2964b35cc",
"1142817c-9007-f0a1-92cd-644935c1fa85",
- "07f16f27-6ef1-b022-1921-02a3baf40d91",
- "1f68f1dc-1d2b-8d04-b7be-0e6fc03fd90a"
+ "07f16f27-6ef1-b022-1921-02a3baf40d91"
],
"owner": 0
},
"name": "Gates",
"description": "Sample PUT, GET, DELETE for PCMM Gates",
"order": [
+ "89d6f693-4b15-44dc-d330-f191ac6780b7",
+ "b8031ee0-b6e0-dcbe-fff3-a924cbe295a4",
+ "e69fb800-4f72-819b-2875-83eecf65dfb1",
"0f01c73b-c17a-b53b-3694-1d746edcd2a2",
"11ef539e-26d3-77ab-3828-321c3f7bf6a3",
"03bebe64-27ff-1df3-5b6b-9237f6c8e229",
"ec1b0f8c-371d-a0bd-ce5f-d30aa31237cb",
"dc932ee9-1acd-9efe-cc83-de07894516bb",
"9d80f0da-4500-e843-31c5-808c91d9249b",
- "b8031ee0-b6e0-dcbe-fff3-a924cbe295a4"
+ "7b027a58-c3a7-dd6a-555f-e5bc3644286c",
+ "e59184fc-4b70-2eb6-eaf5-001fc7f62fbc"
],
"owner": 0,
- "collectionId": "ea685ec8-aa6d-22e9-e501-b608a6705634"
+ "collectionId": "5c57b040-54e7-d5bf-296d-d5a4afdd39bc"
}
],
"timestamp": 1431957020452,
"tests": "",
"currentHelper": "normal",
"helperAttributes": {},
- "time": 1445618651730,
+ "time": 1446221273089,
"name": "Add CCAP 1 - Bad",
"description": "tries to add a CCAP/CMTS to manage with out providing all the required fields",
"collectionId": "5c57b040-54e7-d5bf-296d-d5a4afdd39bc",
"responses": [],
- "rawModeData": "{\n \"ccap\": [{\n \"ccapId\": \"{{ccapId-1}}\",\n \"amId\": {\n \"am-type\": \"1\"\n },\n \"connection\": {\n \"ipAddress\": \"{{ccapIp-1}}\",\n \"port\": \"{{ccapPort-1}}\"\n },\n \"subscriber-subnets\": [\n \"10.32.110.1/24\"\n ],\n \"downstream-scns\": [\n \"extrm_dn\"\n ],\n \"upstream-scns\": [\n \"extrm_up\"\n ]\n }]\n}\n"
+ "rawModeData": "{\n \"ccap\": [{\n \"ccapId\": \"{{ccapId-1}}\",\n \"amId\": {\n },\n \"connection\": {\n \"ipAddress\": \"{{ccapIp-1}}\",\n \"port\": \"{{ccapPort-1}}\"\n },\n \"subscriber-subnets\": [\n \"10.32.110.1/24\"\n ],\n \"downstream-scns\": [\n \"extrm_dn\"\n ],\n \"upstream-scns\": [\n \"extrm_up\"\n ]\n }]\n}\n"
},
{
"id": "0c1e6f25-e33b-e3ed-fc59-02f2b569a9ed",
"collectionId": "5c57b040-54e7-d5bf-296d-d5a4afdd39bc",
"responses": []
},
+ {
+ "id": "7b027a58-c3a7-dd6a-555f-e5bc3644286c",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://{{odlHost}}:{{odlPort}}/restconf/config/packetcable:qos/apps/app/{{appId-classifier}}/subscribers/subscriber/{{subId-classifier}}/gates/gate/{{gateId-classifier}}/",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1447349054220,
+ "name": "Bad - Gate w/ incomplete classifier",
+ "description": "PUT gate with a standard classifier that is missing some data",
+ "collectionId": "5c57b040-54e7-d5bf-296d-d5a4afdd39bc",
+ "responses": [],
+ "rawModeData": "{\n \"gate\": [{\n \"gateId\": \"{{gateId-classifier}}\",\n \"gate-spec\": {\n \"dscp-tos-overwrite\": \"0xa0\",\n \"dscp-tos-mask\": \"0xff\"\n },\n \"traffic-profile\": {\n },\n \"classifier\": {\n \"srcIp\": \"{{srcIp-1a}}\",\n \"dstIp\": \"{{dstIp-1a}}\",\n \"srcPort\": \"{{srcPort-1a}}\",\n \"dstPort\": \"{{dstPort-1a}}\",\n \"tos-byte\": \"0xa0\",\n \"tos-mask\": \"0xe0\"\n }\n }]\n}\n"
+ },
+ {
+ "id": "89d6f693-4b15-44dc-d330-f191ac6780b7",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://{{odlHost}}:{{odlPort}}/restconf/operational/packetcable:qos/",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "GET",
+ "data": [],
+ "dataMode": "params",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1447349051431,
+ "name": "Operational - All Gates",
+ "description": "Retrieves all gates.",
+ "collectionId": "5c57b040-54e7-d5bf-296d-d5a4afdd39bc",
+ "responses": []
+ },
{
"id": "9d80f0da-4500-e843-31c5-808c91d9249b",
"headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
"collectionId": "5c57b040-54e7-d5bf-296d-d5a4afdd39bc",
"responses": []
},
+ {
+ "id": "e59184fc-4b70-2eb6-eaf5-001fc7f62fbc",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://{{odlHost}}:{{odlPort}}/restconf/config/packetcable:qos/apps/app/{{appId-classifier}}/subscribers/subscriber/1.2.3.4/gates/gate/{{gateId-classifier}}",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "PUT",
+ "data": [],
+ "dataMode": "raw",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1447425110789,
+ "name": "Bad - Gate w/ invalid subscriber",
+ "description": "PUT gate with a standard classifier that formed\ncorectly but the subscriber is unknown to the CCAP.",
+ "collectionId": "5c57b040-54e7-d5bf-296d-d5a4afdd39bc",
+ "responses": [],
+ "rawModeData": "{\n \"gate\": [{\n \"gateId\": \"{{gateId-classifier}}\",\n \"gate-spec\": {\n \"dscp-tos-overwrite\": \"0xa0\",\n \"dscp-tos-mask\": \"0xff\"\n },\n \"traffic-profile\": {\n \"service-class-name\": \"{{scnUp}}\"\n },\n \"classifier\": {\n \"srcIp\": \"{{srcIp-1a}}\",\n \"dstIp\": \"{{dstIp-1a}}\",\n \"protocol\": \"0\",\n \"srcPort\": \"{{srcPort-1a}}\",\n \"dstPort\": \"{{dstPort-1a}}\",\n \"tos-byte\": \"0xa0\",\n \"tos-mask\": \"0xe0\"\n }\n }]\n}\n"
+ },
+ {
+ "id": "e69fb800-4f72-819b-2875-83eecf65dfb1",
+ "headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
+ "url": "http://{{odlHost}}:{{odlPort}}/restconf/config/packetcable:qos/",
+ "preRequestScript": "",
+ "pathVariables": {},
+ "method": "DELETE",
+ "data": [],
+ "dataMode": "params",
+ "version": 2,
+ "tests": "",
+ "currentHelper": "normal",
+ "helperAttributes": {},
+ "time": 1447359288221,
+ "name": "All Gates",
+ "description": "Deletes all apps, subscribers, and gates.",
+ "collectionId": "5c57b040-54e7-d5bf-296d-d5a4afdd39bc",
+ "responses": []
+ },
{
"id": "ec1b0f8c-371d-a0bd-ce5f-d30aa31237cb",
"headers": "Authorization: Basic YWRtaW46YWRtaW4=\nContent-Type: application/json\n",
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+import static org.opendaylight.controller.packetcable.provider.DataChangeUtils.collectTypeFromMap;
+import static org.opendaylight.controller.packetcable.provider.DataChangeUtils.collectTypeFromSet;
+import static org.opendaylight.controller.packetcable.provider.DataChangeUtils.logChange;
+import static org.opendaylight.controller.packetcable.provider.DataChangeUtils.relativeComplement;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * @author rvail
+ */
+public abstract class AbstractDataChangeListener<T extends DataObject> implements DataChangeListener {
+
+ protected final Logger logger = LoggerFactory.getLogger(getClass());
+
+ private final Class<T> tClass;
+
+ public AbstractDataChangeListener(Class<T> tClass) {
+ this.tClass = checkNotNull(tClass);
+ }
+
+ @Override
+ public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> asyncDataChangeEvent) {
+ logger.debug("############{}.onDataChanged()", getClass().getSimpleName());
+ logChange(logger, asyncDataChangeEvent);
+
+ final Map<InstanceIdentifier<?>, DataObject> allCreatedData = asyncDataChangeEvent.getCreatedData();
+ final Map<InstanceIdentifier<?>, DataObject> allOriginalData = asyncDataChangeEvent.getOriginalData();
+ final Map<InstanceIdentifier<?>, DataObject> allUpdatedData = asyncDataChangeEvent.getUpdatedData();
+
+ // UpdatedData also contains all data that was created, remove it to get the set of only updated data
+ final Map<InstanceIdentifier<?>, DataObject> trueUpdatedData =
+ relativeComplement(allCreatedData, allUpdatedData);
+ final Map<InstanceIdentifier<?>, DataObject> trueOriginalData =
+ relativeComplement(allCreatedData, allOriginalData);
+
+ if (!allCreatedData.isEmpty()) {
+ final Map<InstanceIdentifier<T>, T> createdTs = collectTypeFromMap(tClass, allCreatedData);
+
+ if (createdTs.isEmpty()) {
+ // this should not happen since this object only listens for changes in one tree
+ logger.warn("Expected created {}(s) but none were found: {}", tClass.getSimpleName(), allCreatedData);
+ }
+ else {
+ handleCreatedData(createdTs);
+ }
+ }
+
+ if (!trueUpdatedData.isEmpty()) {
+ final Map<InstanceIdentifier<T>, T> updatedTs = collectTypeFromMap(tClass, trueUpdatedData);
+ if (updatedTs.isEmpty()) {
+ // this should not happen since this object should only listen for changes in its tree
+ logger.warn("Expected updated {}(s) but none were found: {}", tClass.getSimpleName(), trueUpdatedData);
+ }
+ else {
+
+ final Map<InstanceIdentifier<T>, T> originalTs = collectTypeFromMap(tClass, trueOriginalData);
+ for (InstanceIdentifier<T> iid : updatedTs.keySet()) {
+ if (!originalTs.containsKey(iid)) {
+ logger.warn("No original data for updated object {}", iid);
+ }
+ }
+
+ handleUpdatedData(updatedTs, originalTs);
+ }
+ }
+
+ final Set<InstanceIdentifier<?>> allRemovedPaths = asyncDataChangeEvent.getRemovedPaths();
+ if (!allRemovedPaths.isEmpty()) {
+ final Set<InstanceIdentifier<T>> removedTPaths = collectTypeFromSet(tClass, allRemovedPaths);
+ if (removedTPaths.isEmpty()) {
+ // this should not happen since this object should only listen for changes in its tree
+ logger.warn("Expected removed {} but none were found: {}", tClass.getSimpleName(), allRemovedPaths);
+ }
+
+ Map<InstanceIdentifier<T>, T> originalTData = Maps.newHashMapWithExpectedSize(removedTPaths.size());
+ for (InstanceIdentifier<T> iid : removedTPaths) {
+ if (allOriginalData.containsKey(iid)) {
+
+ originalTData.put(iid, (T) allOriginalData.get(iid));
+ }
+ }
+
+ handleRemovedData(removedTPaths, originalTData);
+ }
+ }
+
+ protected abstract void handleCreatedData(final Map<InstanceIdentifier<T>, T> createdData);
+
+ protected abstract void handleUpdatedData(final Map<InstanceIdentifier<T>, T> updatedData,
+ final Map<InstanceIdentifier<T>, T> originalData);
+
+ protected abstract void handleRemovedData(final Set<InstanceIdentifier<T>> removedPaths,
+ final Map<InstanceIdentifier<T>, T> originalData);
+
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider;
+
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import java.util.Map;
+import java.util.Set;
+import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+
+/**
+ * @author rvail
+ */
+public class DataChangeUtils {
+
+ @SuppressWarnings("unchecked")
+ public static <T extends DataObject> Map<InstanceIdentifier<T>, T> collectTypeFromMap(Class<T> tClass,
+ Map<InstanceIdentifier<?>, DataObject> map) {
+ Map<InstanceIdentifier<T>, T> result = Maps.newHashMap();
+
+ for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : map.entrySet()) {
+ if (tClass.isAssignableFrom(entry.getValue().getImplementedInterface())) {
+ final InstanceIdentifier<T> tIID = (InstanceIdentifier<T>) entry.getKey();
+ final T tObj = (T) entry.getValue();
+ result.put(tIID, tObj);
+ }
+ }
+
+ return result;
+ }
+
+ public static <T extends DataObject> Set<InstanceIdentifier<T>> collectTypeFromSet(Class<T> tClass,
+ Set<InstanceIdentifier<?>> set) {
+ Set<InstanceIdentifier<T>> result = Sets.newHashSet();
+
+ for (InstanceIdentifier<?> iid : set) {
+ if (tClass.isAssignableFrom(iid.getTargetType())) {
+ @SuppressWarnings("unchecked")
+ final InstanceIdentifier<T> tIID = (InstanceIdentifier<T>) iid;
+ result.add(tIID);
+ }
+ }
+
+ return result;
+ }
+
+ /**
+ * Computes the relative complement of A in B. (aka get everything in B that is not in A)
+ * @param setA The first set
+ * @param setB The second set
+ * @return the relative complement
+ */
+ public static Map<InstanceIdentifier<?>, DataObject> relativeComplement(Map<InstanceIdentifier<?>, DataObject> setA,
+ Map<InstanceIdentifier<?>, DataObject> setB){
+
+ Map<InstanceIdentifier<?>, DataObject> result = Maps.newHashMap();
+
+ for (InstanceIdentifier<?> iid: setB.keySet()) {
+ if (!setA.containsKey(iid)) {
+ result.put(iid, setB.get(iid));
+ }
+ }
+
+ return result;
+ }
+
+
+
+ public static void logChange(final Logger logger, final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
+ if (!logger.isDebugEnabled()) {
+ return;
+ }
+
+ StringBuilder sb = new StringBuilder();
+
+ appendMap(sb, "CREATED", change.getCreatedData());
+ appendSet(sb, "REMOVED", change.getRemovedPaths());
+ appendMap(sb, "UPDATED", change.getUpdatedData());
+ appendMap(sb, "ORIGINAL", change.getOriginalData());
+
+ logger.debug("onDataChanged data:\n{}", sb.toString());
+ }
+
+
+ private static <E> void appendSet(StringBuilder sb, String name, Set<E> set) {
+ sb.append("====").append(name).append("====\n");
+ if (set == null || set.isEmpty()) {
+ sb.append("None\n");
+ } else {
+ for (E data : set) {
+ sb.append(data).append("\n");
+ sb.append("----------\n");
+ }
+ }
+ sb.append("===============\n");
+ }
+
+ private static void appendMap(StringBuilder sb, String name, Map<InstanceIdentifier<?>, DataObject> dataMap) {
+ sb.append("====").append(name).append("====\n");
+ if (dataMap == null || dataMap.isEmpty()) {
+ sb.append("None\n");
+ } else {
+ for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : dataMap.entrySet()) {
+ sb.append(entry.getValue()).append("\n");
+ sb.append("----------\n");
+ }
+ }
+ sb.append("===============\n");
+ }
+}
package org.opendaylight.controller.packetcable.provider;
+import com.google.common.base.Optional;
+import com.google.common.util.concurrent.CheckedFuture;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
+import org.opendaylight.controller.md.sal.binding.api.ReadOnlyTransaction;
import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.ReadFailedException;
import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.yangtools.yang.binding.DataObject;
import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import com.google.common.util.concurrent.CheckedFuture;
-
public class MdsalUtils {
private static final Logger LOG = LoggerFactory.getLogger(MdsalUtils.class);
private DataBroker databroker = null;
/**
* Class constructor setting the data broker.
*
- * @param dataBroker the {@link org.opendaylight.controller.md.sal.binding.api.DataBroker}
+ * @param dataBroker the {@link DataBroker}
*/
public MdsalUtils(DataBroker dataBroker) {
this.databroker = dataBroker;
* @param <D> the data object type
* @return the result of the request
*/
- public <D extends org.opendaylight.yangtools.yang.binding.DataObject> boolean delete(
+ public <D extends DataObject> boolean delete(
final LogicalDatastoreType store, final InstanceIdentifier<D> path) {
boolean result = false;
final WriteTransaction transaction = databroker.newWriteOnlyTransaction();
* @param <D> the data object type
* @return the result of the request
*/
- public <D extends org.opendaylight.yangtools.yang.binding.DataObject> boolean merge(
+ public <D extends DataObject> boolean merge(
final LogicalDatastoreType logicalDatastoreType, final InstanceIdentifier<D> path, D data) {
boolean result = false;
final WriteTransaction transaction = databroker.newWriteOnlyTransaction();
}
return result;
}
+
+ /**
+ * Execures a read as a blocking transaction.
+ *
+ * @param logicalDatastoreType which datastore to read from
+ * @param path The path to read
+ * @param <D> the DataObject type
+ * @return an Optional containing the object.
+ */
+ public <D extends DataObject> Optional<D> read(final LogicalDatastoreType logicalDatastoreType, final InstanceIdentifier<D> path) {
+ final ReadOnlyTransaction readOnlyTransaction = databroker.newReadOnlyTransaction();
+
+ CheckedFuture<Optional<D>, ReadFailedException> future = readOnlyTransaction.read(logicalDatastoreType, path);
+
+ try {
+ return future.checkedGet();
+ } catch (ReadFailedException e) {
+ LOG.error("Failed to read {} at path {}", logicalDatastoreType, path, e);
+ }
+
+ return Optional.absent();
+ }
+
+ /**
+ * Executes put as a blocking transaction.
+ *
+ * @param logicalDatastoreType {@link LogicalDatastoreType} which should be modified
+ * @param path {@link InstanceIdentifier} for path to read
+ * @param <D> the data object type
+ * @return the result of the request
+ */
+ public <D extends DataObject> boolean put(
+ final LogicalDatastoreType logicalDatastoreType, final InstanceIdentifier<D> path, D data) {
+ boolean result = false;
+ final WriteTransaction transaction = databroker.newWriteOnlyTransaction();
+ transaction.put(logicalDatastoreType, path, data, true);
+ CheckedFuture<Void, TransactionCommitFailedException> future = transaction.submit();
+ try {
+ future.checkedGet();
+ result = true;
+ } catch (TransactionCommitFailedException e) {
+ LOG.warn("Failed to merge {} ", path, e);
+ }
+ return result;
+ }
}
-/**
- * Build PCMM gate requests from API QoS Gate objects
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
*/
+
package org.opendaylight.controller.packetcable.provider;
+import java.net.Inet4Address;
+import java.net.Inet6Address;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ServiceFlowDirection;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.TosByte;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.AmId;
import org.pcmm.gates.IGateSpec.Direction;
import org.pcmm.gates.IIPv6Classifier.FlowLabel;
import org.pcmm.gates.ITrafficProfile;
-import org.pcmm.gates.impl.*;
+import org.pcmm.gates.impl.AMID;
+import org.pcmm.gates.impl.DOCSISServiceClassNameTrafficProfile;
+import org.pcmm.gates.impl.GateID;
+import org.pcmm.gates.impl.PCMMError;
+import org.pcmm.gates.impl.PCMMGateReq;
+import org.pcmm.gates.impl.SubscriberID;
+import org.pcmm.gates.impl.TransactionID;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
-import java.net.Inet4Address;
-import java.net.Inet6Address;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
/**
- * PacketCable data processor
+ * Build PCMM gate requests from API QoS Gate objects
*/
public class PCMMGateReqBuilder {
- private Logger logger = LoggerFactory.getLogger(PCMMGateReqBuilder.class);
+ private final Logger logger = LoggerFactory.getLogger(PCMMGateReqBuilder.class);
private GateID gateID = null;
private AMID amid = null;
/*
- * (c) 2015 Cable Television Laboratories, Inc. All rights reserved.
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
*/
-
package org.opendaylight.controller.packetcable.provider;
import com.google.common.collect.Maps;
*/
@ThreadSafe
public class PCMMService {
- private Logger logger = LoggerFactory.getLogger(PCMMService.class);
-
- private final Ccap ccap;
- private final IpAddress ipAddr;
- private final PortNumber portNum;
- protected final CcapClient ccapClient;
- protected Map<String, PCMMGateReq> gateRequests = Maps.newConcurrentMap();
-
- private final short clientType;
-
- public PCMMService(final short clientType, final Ccap ccap) {
- this.clientType = clientType;
- this.ccap = ccap;
- ipAddr = ccap.getConnection().getIpAddress();
- portNum = ccap.getConnection().getPort();
- ccapClient = new CcapClient(ipAddr, portNum);
- logger.info("Attempting to add CCAP with ID {} @ {}:{}", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(),
- portNum.getValue());
- }
-
- public void disconect() {
- ccapClient.disconnect();
- }
-
- // TODO - try and change the return to something other than a String to be parsed to determine success
- public String addCcap() {
- ccapClient.connect();
- if (ccapClient.isConnected) {
- logger.info("Connected to CCAP with ID - " + ccap.getCcapId());
- return String.format("200 OK - CCAP %s connected @ %s:%d", ccap.getCcapId(),
- ipAddr.getIpv4Address().getValue(), portNum.getValue());
- } else {
- return String.format("404 Not Found - CCAP %s failed to connect @ %s:%d - %s",
- ccap.getCcapId(),
- ipAddr.getIpv4Address().getValue(), portNum.getValue(), ccapClient.errMessage);
- }
- }
-
- // TODO - Consider creating an object to return that contains a success flag, message, and gate ID or gate object
- public String sendGateSet(final String gatePathStr, final InetAddress subId, final Gate qosGate,
- final ServiceFlowDirection scnDir) {
- logger.info("Sending gate to CCAP with ID - " + ccap.getCcapId());
- // assemble the gate request for this subId
- final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
- gateBuilder.setAmId(ccap.getAmId());
- gateBuilder.setSubscriberId(subId);
- // force gateSpec.Direction to align with SCN direction
- final ServiceClassName scn = qosGate.getTrafficProfile().getServiceClassName();
- if (scn != null) {
- gateBuilder.setGateSpec(qosGate.getGateSpec(), scnDir);
- } else {
- // not an SCN gate
- gateBuilder.setGateSpec(qosGate.getGateSpec(), null);
- }
- gateBuilder.setTrafficProfile(qosGate.getTrafficProfile());
-
- // pick a classifier type (only one for now)
- if (qosGate.getClassifier() != null) {
- gateBuilder.setClassifier(qosGate.getClassifier());
- } else if (qosGate.getExtClassifier() != null) {
- gateBuilder.setExtClassifier(qosGate.getExtClassifier());
- } else if (qosGate.getIpv6Classifier() != null) {
- gateBuilder.setIpv6Classifier(qosGate.getIpv6Classifier());
- }
- // assemble the final gate request
- final PCMMGateReq gateReq = gateBuilder.build();
-
- if (gateRequests.get(gatePathStr) == null) {
- // and remember it
- gateRequests.put(gatePathStr, gateReq);
- // and send it to the CCAP
- ccapClient.sendGateSet(gateReq);
- // and wait for the COPS response to complete processing gate request
- try {
- // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
- // TODO - handle this synchronization.
- // TODO - if not changing this, may want to make this timeout configurable
- synchronized(gateReq) {
- logger.info("Waiting 5000ms for gate request to be updated");
- gateReq.wait(5000);
- logger.debug("Gate request error - " + gateReq.getError());
- logger.debug("Gate request ID - " + gateReq.getGateID());
- }
- } catch (Exception e) {
- logger.error("PCMMService: sendGateSet(): gate response timeout exceeded for "
- + gatePathStr + '/' + gateReq, e);
- return String.format("408 Request Timeout - gate response timeout exceeded for %s/%s",
- ccap.getCcapId(), gatePathStr);
- }
- if (gateReq.getError() != null) {
- logger.error("PCMMService: sendGateSet(): returned error: {}",
- gateReq.getError().toString());
- return String.format("404 Not Found - sendGateSet for %s/%s returned error - %s",
- ccap.getCcapId(), gatePathStr, gateReq.getError().toString());
- } else {
- if (gateReq.getGateID() != null) {
- logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
- gateReq.getGateID().getGateID()));
- return String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
- ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID());
- } else {
- logger.info("PCMMService: sendGateSet(): no gateId returned:");
- return String.format("404 Not Found - sendGateSet for %s/%s no gateId returned",
- ccap.getCcapId(), gatePathStr);
- }
- }
- } else {
- logger.info("PCMMService: sendGateSet(): no gateId returned:");
- return String.format("404 Not Found - sendGateSet for %s/%s already exists",
- ccap.getCcapId(), gatePathStr);
- }
- }
-
- public Boolean sendGateDelete(final String gatePathStr) {
- logger.info("sendGateDelete() - " + ccap);
- // recover the original gate request
- final PCMMGateReq gateReq = gateRequests.remove(gatePathStr);
- if (gateReq != null) {
- ccapClient.sendGateDelete(gateReq);
- // and wait for the response to complete
- try {
- // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
- // TODO - handle this synchronization.
- synchronized(gateReq) {
- gateReq.wait(1000);
- }
- } catch (InterruptedException e) {
- logger.error("PCMMService: sendGateDelete(): gate response timeout exceeded for {}/{}",
- gatePathStr, gateReq);
- }
- if (gateReq.getError() != null) {
- logger.warn("PCMMService: sendGateDelete(): returned error: {}", gateReq.getError().toString());
- return false;
- } else {
- if (gateReq.getGateID() != null) {
- logger.info(String.format("PCMMService: sendGateDelete(): deleted GateId %08x: ", gateReq.getGateID().getGateID()));
- } else {
- logger.error("PCMMService: sendGateDelete(): deleted but no gateId returned");
- }
- return true;
- }
- } else {
- logger.warn("Attempt to delete non-existent gate with path - " + gatePathStr);
- return false;
- }
- }
-
- /**
- * Used to interface with a CCAP (including CMTSs)
- */
- protected class CcapClient {
- public final PCMMPdpDataProcess pcmmProcess;
- public final PCMMPdpAgent pcmmPdp;
-
- private final String ipv4;
- private final Integer port;
-
- // Needs to be initialized in connect() method else would be final
- protected transient PCMMPdpMsgSender pcmmSender;
-
- private transient Boolean isConnected = false;
- private transient String errMessage = null;
-
- /**
- * Constructor
- * @param ccapIp - the IP of the CCAP to manage
- * @param portNum - the port number of the CCAP to manage
- */
- public CcapClient(final IpAddress ccapIp, final PortNumber portNum) {
- ipv4 = ccapIp.getIpv4Address().getValue();
- if (portNum != null) port = portNum.getValue();
- else port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT;
- // TODO FIXME - if this object is not null, gate processing will not work correctly
- // TODO see - PCMMPdpReqStateMan#processReport() where the report type is success and the process is null
- // pcmmProcess = new PCMMPdpDataProcess();
- pcmmProcess = null;
- pcmmPdp = new PCMMPdpAgent(ipv4, port, clientType, pcmmProcess);
- }
-
- /**
- * Starts the connection to the CCAP
- */
- public void connect( ) {
- logger.info("Attempting to connect to host: " + ipv4 + " port: " + port);
- try {
- pcmmPdp.connect();
-
- // Cannot instantiate until after pcmmPdp.connect() is called as this is where the client handle is created
- pcmmSender = new PCMMPdpMsgSender(clientType, pcmmPdp.getClientHandle(), pcmmPdp.getSocket());
-
- isConnected = true;
- } catch (Exception e) {
- isConnected = false;
- logger.error("Failed to connect to host: " + ipv4 + " port: " + port, e);
- errMessage = e.getMessage();
- }
- }
-
- public void disconnect() {
- logger.info("CcapClient: disconnect(): {}:{}", ipv4, port);
- pcmmPdp.disconnect(new COPSError(ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA));
- isConnected = false;
- }
-
- // TODO - consider returning a new PCMMGateReq object or a future here instead of setting the ID on the old
- // TODO - request by reference which makes the code more convoluted thus making issues more difficult to track down.
- public Boolean sendGateSet(final PCMMGateReq gateReq) {
- logger.info("CcapClient: sendGateSet(): {}:{} => {}", ipv4, port, gateReq);
- try {
- pcmmSender.sendGateSet(gateReq);
-
- // TODO - determine if this is the correct place to perform this operation as this currently is the
- // TODO - place where the gate ID can be set on the gateReq object
- // pcmmSender.handleGateReport(pcmmPdp.getSocket());
- } catch (COPSPdpException e) {
- logger.error("CcapClient: sendGateSet(): {}:{} => {} FAILED:", ipv4, port, gateReq, e);
- }
- // and save it back to the gateRequest object for gate delete later
- gateReq.setGateID(pcmmSender.getGateID());
-
- // TODO - determine why this method is always returning true???
- return true;
- }
-
- public Boolean sendGateDelete(final PCMMGateReq gateReq) {
- logger.info("CcapClient: sendGateDelete(): {}:{} => {}", ipv4, port, gateReq);
- try {
- pcmmSender.sendGateDelete(gateReq);
- } catch (COPSPdpException e) {
- logger.error("CcapClient: sendGateDelete(): {}:{} => {} FAILED: {}", ipv4, port, gateReq, e.getMessage());
- }
- return true;
- }
- }
+ private final Logger logger = LoggerFactory.getLogger(PCMMService.class);
+
+ private final Ccap ccap;
+ private final IpAddress ipAddr;
+ private final PortNumber portNum;
+ protected final CcapClient ccapClient;
+ protected Map<String, PCMMGateReq> gateRequests = Maps.newConcurrentMap();
+
+ private final short clientType;
+
+ public PCMMService(final short clientType, final Ccap ccap) {
+ this.clientType = clientType;
+ this.ccap = ccap;
+ ipAddr = ccap.getConnection().getIpAddress();
+ portNum = ccap.getConnection().getPort();
+ ccapClient = new CcapClient(ipAddr, portNum);
+ logger.info("Attempting to add CCAP with ID {} @ {}:{}", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(),
+ portNum.getValue());
+ }
+
+ public void disconect() {
+ ccapClient.disconnect();
+ }
+
+ // TODO - try and change the return to something other than a String to be parsed to determine success
+ public String addCcap() {
+ ccapClient.connect();
+ if (ccapClient.isConnected) {
+ logger.info("Connected to CCAP with ID - " + ccap.getCcapId());
+ return String
+ .format("200 OK - CCAP %s connected @ %s:%d", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(),
+ portNum.getValue());
+ } else {
+ return String.format("404 Not Found - CCAP %s failed to connect @ %s:%d - %s", ccap.getCcapId(),
+ ipAddr.getIpv4Address().getValue(), portNum.getValue(), ccapClient.errMessage);
+ }
+ }
+
+ public class GateSetStatus {
+ private boolean didSucceed = false;
+ private String message = "";
+ private String copsGateId = "";
+
+ public boolean didSucceed() {
+ return didSucceed;
+ }
+
+ void setDidSucceed(final boolean didSucceed) {
+ this.didSucceed = didSucceed;
+ }
+
+ public String getMessage() {
+ return message;
+ }
+
+ void setMessage(final String message) {
+ this.message = message;
+ }
+
+ public String getCopsGateId() {
+ return copsGateId;
+ }
+
+ void setCopsGateId(final String copsGateId) {
+ this.copsGateId = copsGateId;
+ }
+
+ }
+
+ public GateSetStatus sendGateSet(final String gatePathStr, final InetAddress subId, final Gate qosGate,
+ final ServiceFlowDirection scnDir) {
+
+ GateSetStatus status = new GateSetStatus();
+
+ logger.info("Sending gate to CCAP with ID - " + ccap.getCcapId());
+
+ // assemble the gate request for this subId
+ final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
+ gateBuilder.setAmId(ccap.getAmId());
+ gateBuilder.setSubscriberId(subId);
+
+ // force gateSpec.Direction to align with SCN direction
+ final ServiceClassName scn = qosGate.getTrafficProfile().getServiceClassName();
+ if (scn != null) {
+ gateBuilder.setGateSpec(qosGate.getGateSpec(), scnDir);
+ } else {
+ // not an SCN gate
+ gateBuilder.setGateSpec(qosGate.getGateSpec(), null);
+ }
+ gateBuilder.setTrafficProfile(qosGate.getTrafficProfile());
+
+ // pick a classifier type (only one for now)
+ if (qosGate.getClassifier() != null) {
+ gateBuilder.setClassifier(qosGate.getClassifier());
+ } else if (qosGate.getExtClassifier() != null) {
+ gateBuilder.setExtClassifier(qosGate.getExtClassifier());
+ } else if (qosGate.getIpv6Classifier() != null) {
+ gateBuilder.setIpv6Classifier(qosGate.getIpv6Classifier());
+ }
+
+ // assemble the final gate request
+ final PCMMGateReq gateReq = gateBuilder.build();
+
+ if (gateRequests.get(gatePathStr) == null) {
+ // and remember it
+ gateRequests.put(gatePathStr, gateReq);
+ // and send it to the CCAP
+ ccapClient.sendGateSet(gateReq);
+ // and wait for the COPS response to complete processing gate request
+ try {
+ // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
+ // TODO - handle this synchronization.
+ // TODO - if not changing this, may want to make this timeout configurable
+ synchronized (gateReq) {
+ logger.info("Waiting 5000ms for gate request to be updated");
+ gateReq.wait(5000);
+ logger.debug("Gate request error - " + gateReq.getError());
+ logger.debug("Gate request ID - " + gateReq.getGateID());
+ }
+ } catch (Exception e) {
+ logger.error(
+ "PCMMService: sendGateSet(): gate response timeout exceeded for " + gatePathStr + '/' + gateReq,
+ e);
+ status.setDidSucceed(false);
+ status.setMessage(String.format("408 Request Timeout - gate response timeout exceeded for %s/%s", ccap.getCcapId(),
+ gatePathStr));
+ return status;
+ }
+
+
+ if (gateReq.getError() != null) {
+ status.setDidSucceed(false);
+ status.setMessage(
+ String.format("404 Not Found - sendGateSet for %s/%s returned error - %s", ccap.getCcapId(),
+ gatePathStr, gateReq.getError().toString()));
+
+ logger.error("PCMMService: sendGateSet(): returned error: {}", gateReq.getError().toString());
+ } else {
+ if (gateReq.getGateID() != null) {
+ status.setDidSucceed(true);
+ status.setCopsGateId(String.format("%08x", gateReq.getGateID().getGateID()));
+ status.setMessage(String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
+ ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID()) );
+ logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
+ gateReq.getGateID().getGateID()));
+ } else {
+ status.setDidSucceed(false);
+ status.setMessage(
+ String.format("404 Not Found - sendGateSet for %s/%s no gateId returned", ccap.getCcapId(),
+ gatePathStr));
+
+ logger.info("PCMMService: sendGateSet(): no gateId returned:");
+ }
+ }
+ } else {
+ logger.info("PCMMService: sendGateSet(): no gateId returned:");
+ status.setMessage(String.format("404 Not Found - sendGateSet for %s/%s already exists", ccap.getCcapId(), gatePathStr));
+ }
+
+ return status;
+ }
+
+ public Boolean sendGateDelete(final String gatePathStr) {
+ logger.info("sendGateDelete() - " + ccap);
+ // recover the original gate request
+ final PCMMGateReq gateReq = gateRequests.remove(gatePathStr);
+ if (gateReq != null) {
+ ccapClient.sendGateDelete(gateReq);
+ // and wait for the response to complete
+ try {
+ // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
+ // TODO - handle this synchronization.
+ synchronized (gateReq) {
+ gateReq.wait(1000);
+ }
+ } catch (InterruptedException e) {
+ logger.error("PCMMService: sendGateDelete(): gate response timeout exceeded for {}/{}", gatePathStr,
+ gateReq);
+ }
+ if (gateReq.getError() != null) {
+ logger.warn("PCMMService: sendGateDelete(): returned error: {}", gateReq.getError().toString());
+ return false;
+ } else {
+ if (gateReq.getGateID() != null) {
+ logger.info(String.format("PCMMService: sendGateDelete(): deleted GateId %08x: ",
+ gateReq.getGateID().getGateID()));
+ } else {
+ logger.error("PCMMService: sendGateDelete(): deleted but no gateId returned");
+ }
+ return true;
+ }
+ } else {
+ logger.warn("Attempt to delete non-existent gate with path - " + gatePathStr);
+ return false;
+ }
+ }
+
+ /**
+ * Used to interface with a CCAP (including CMTSs)
+ */
+ protected class CcapClient {
+ public final PCMMPdpDataProcess pcmmProcess;
+ public final PCMMPdpAgent pcmmPdp;
+
+ private final String ipv4;
+ private final Integer port;
+
+ // Needs to be initialized in connect() method else would be final
+ protected transient PCMMPdpMsgSender pcmmSender;
+
+ private transient Boolean isConnected = false;
+ private transient String errMessage = null;
+
+ /**
+ * Constructor
+ *
+ * @param ccapIp
+ * - the IP of the CCAP to manage
+ * @param portNum
+ * - the port number of the CCAP to manage
+ */
+ public CcapClient(final IpAddress ccapIp, final PortNumber portNum) {
+ ipv4 = ccapIp.getIpv4Address().getValue();
+ if (portNum != null) {
+ port = portNum.getValue();
+ } else {
+ port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT;
+ }
+ // TODO FIXME - if this object is not null, gate processing will not work correctly
+ // TODO see - PCMMPdpReqStateMan#processReport() where the report type is success and the process is null
+ // pcmmProcess = new PCMMPdpDataProcess();
+ pcmmProcess = null;
+ pcmmPdp = new PCMMPdpAgent(ipv4, port, clientType, pcmmProcess);
+ }
+
+ /**
+ * Starts the connection to the CCAP
+ */
+ public void connect() {
+ logger.info("Attempting to connect to host: " + ipv4 + " port: " + port);
+ try {
+ pcmmPdp.connect();
+
+ // Cannot instantiate until after pcmmPdp.connect() is called as this is where the client handle is created
+ pcmmSender = new PCMMPdpMsgSender(clientType, pcmmPdp.getClientHandle(), pcmmPdp.getSocket());
+
+ isConnected = true;
+ } catch (Exception e) {
+ isConnected = false;
+ logger.error("Failed to connect to host: " + ipv4 + " port: " + port, e);
+ errMessage = e.getMessage();
+ }
+ }
+
+ public void disconnect() {
+ logger.info("CcapClient: disconnect(): {}:{}", ipv4, port);
+ pcmmPdp.disconnect(new COPSError(ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA));
+ isConnected = false;
+ }
+
+ // TODO - consider returning a new PCMMGateReq object or a future here instead of setting the ID on the old
+ // TODO - request by reference which makes the code more convoluted thus making issues more difficult to track down.
+ public Boolean sendGateSet(final PCMMGateReq gateReq) {
+ logger.info("CcapClient: sendGateSet(): {}:{} => {}", ipv4, port, gateReq);
+ try {
+ pcmmSender.sendGateSet(gateReq);
+
+ // TODO - determine if this is the correct place to perform this operation as this currently is the
+ // TODO - place where the gate ID can be set on the gateReq object
+ // pcmmSender.handleGateReport(pcmmPdp.getSocket());
+ } catch (COPSPdpException e) {
+ logger.error("CcapClient: sendGateSet(): {}:{} => {} FAILED:", ipv4, port, gateReq, e);
+ }
+ // and save it back to the gateRequest object for gate delete later
+ gateReq.setGateID(pcmmSender.getGateID());
+
+ // TODO - determine why this method is always returning true???
+ return true;
+ }
+
+ public Boolean sendGateDelete(final PCMMGateReq gateReq) {
+ logger.info("CcapClient: sendGateDelete(): {}:{} => {}", ipv4, port, gateReq);
+ try {
+ pcmmSender.sendGateDelete(gateReq);
+ } catch (COPSPdpException e) {
+ logger.error("CcapClient: sendGateDelete(): {}:{} => {} FAILED: {}", ipv4, port, gateReq,
+ e.getMessage());
+ }
+ return true;
+ }
+ }
}
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
package org.opendaylight.controller.packetcable.provider;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.google.common.util.concurrent.CheckedFuture;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
+import java.util.concurrent.Executor;
+import java.util.concurrent.Executors;
+import javax.annotation.Nonnull;
import javax.annotation.concurrent.ThreadSafe;
import org.opendaylight.controller.md.sal.binding.api.DataBroker;
import org.opendaylight.controller.md.sal.binding.api.DataChangeListener;
-import org.opendaylight.controller.md.sal.common.api.data.AsyncDataChangeEvent;
+import org.opendaylight.controller.md.sal.binding.api.WriteTransaction;
import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
+import org.opendaylight.controller.md.sal.common.api.data.TransactionCommitFailedException;
+import org.opendaylight.controller.packetcable.provider.validation.DataValidator;
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.Validator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.CcapsValidatorProviderFactory;
+import org.opendaylight.controller.packetcable.provider.validation.impl.QosValidatorProviderFactory;
import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext;
import org.opendaylight.controller.sal.binding.api.BindingAwareProvider;
import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.Qos;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ServiceClassName;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ServiceFlowDirection;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.Connection;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.ConnectionBuilder;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.Ccap;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.CcapKey;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.CcapBuilder;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.Apps;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.App;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.AppBuilder;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.AppKey;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.Subscribers;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.SubscribersBuilder;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.Subscriber;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.SubscriberBuilder;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.SubscriberKey;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.Gates;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.GatesBuilder;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.GateBuilder;
import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.GateKey;
import org.opendaylight.yangtools.concepts.ListenerRegistration;
import org.opendaylight.yangtools.yang.binding.DataObject;
/**
* Called by ODL framework to start this bundle.
- *
+ * <p>
* This class is responsible for processing messages received from ODL's restconf interface.
* TODO - Remove some of these state maps and move some of this into the PCMMService
*/
@ThreadSafe
-public class PacketcableProvider implements BindingAwareProvider, DataChangeListener, AutoCloseable {
+public class PacketcableProvider implements BindingAwareProvider, AutoCloseable {
private static final Logger logger = LoggerFactory.getLogger(PacketcableProvider.class);
- // keys to the /restconf/config/packetcable:ccap and /restconf/config/packetcable:qos config datastore
- public static final InstanceIdentifier<Ccaps> ccapIID = InstanceIdentifier.builder(Ccaps.class).build();
- public static final InstanceIdentifier<Qos> qosIID = InstanceIdentifier.builder(Qos.class).build();
-
- /**
- * The ODL object used to broker messages throughout the framework
- */
- private DataBroker dataBroker;
-
- private MdsalUtils mdsalUtils;
-
- private ListenerRegistration<DataChangeListener> ccapDataChangeListenerRegistration;
- private ListenerRegistration<DataChangeListener> qosDataChangeListenerRegistration;
+ // keys to the /restconf/config/packetcable:ccaps and /restconf/config/packetcable:qos config datastore
+ private static final InstanceIdentifier<Ccaps> ccapsIID = InstanceIdentifier.builder(Ccaps.class).build();
+ private static final InstanceIdentifier<Qos> qosIID = InstanceIdentifier.builder(Qos.class).build();
// TODO - Revisit these maps and remove the ones no longer necessary
private final Map<String, Ccap> ccapMap = new ConcurrentHashMap<>();
private final Map<ServiceClassName, List<Ccap>> downstreamScnMap = new ConcurrentHashMap<>();
private final Map<ServiceClassName, List<Ccap>> upstreamScnMap = new ConcurrentHashMap<>();
+ private final Executor executor = Executors.newSingleThreadExecutor();
+
/**
* Holds a PCMMService object for each CCAP being managed.
*/
private final Map<String, PCMMService> pcmmServiceMap = new ConcurrentHashMap<>();
+ /**
+ * The ODL object used to broker messages throughout the framework
+ */
+ private DataBroker dataBroker;
+ private MdsalUtils mdsalUtils;
+
+ // Data change listeners/registrations
+ private final CcapsDataChangeListener ccapsDataChangeListener = new CcapsDataChangeListener();
+ private final QosDataChangeListener qosDataChangeListener = new QosDataChangeListener();
+
+ private ListenerRegistration<DataChangeListener> ccapsDataChangeListenerRegistration;
+ private ListenerRegistration<DataChangeListener> qosDataChangeListenerRegistration;
+
/**
* Constructor
*/
@Override
public void onSessionInitiated(ProviderContext session) {
logger.info("Packetcable Session Initiated");
+ logger.info("logging levels: error={}, warn={}, info={}, debug={}, trace={}", logger.isErrorEnabled(),
+ logger.isWarnEnabled(), logger.isInfoEnabled(), logger.isDebugEnabled(), logger.isTraceEnabled());
- dataBroker = session.getSALService(DataBroker.class);
+ dataBroker = session.getSALService(DataBroker.class);
mdsalUtils = new MdsalUtils(dataBroker);
- ccapDataChangeListenerRegistration =
- dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
- PacketcableProvider.ccapIID, this, DataBroker.DataChangeScope.SUBTREE );
+ ccapsDataChangeListenerRegistration = dataBroker
+ .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, ccapsIID.child(Ccap.class),
+ ccapsDataChangeListener, DataBroker.DataChangeScope.SUBTREE);
+
+ qosDataChangeListenerRegistration = dataBroker
+ .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, PacketcableProvider.qosIID.child(Apps.class).child(App.class),
+ qosDataChangeListener, DataBroker.DataChangeScope.SUBTREE);
+
+ // Add empty top level elements
+// for (LogicalDatastoreType datastoreType : LogicalDatastoreType.values()) {
+// WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+// writeTransaction.put(datastoreType, ccapsIID, new CcapsBuilder().build());
+// CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
+// try {
+// future.checkedGet();
+// } catch (TransactionCommitFailedException e) {
+// logger.error("Failed to initialise top level ccaps in datastore {}", datastoreType, e);
+// }
+// writeTransaction = dataBroker.newWriteOnlyTransaction();
+// writeTransaction.put(datastoreType, qosIID, new QosBuilder().build());
+// future = writeTransaction.submit();
+// try {
+// future.checkedGet();
+// } catch (TransactionCommitFailedException e) {
+// logger.error("Failed to initialise top level qos in datastore {}", datastoreType, e);
+// }
+// }
+
- qosDataChangeListenerRegistration =
- dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION,
- PacketcableProvider.qosIID, this, DataBroker.DataChangeScope.SUBTREE );
}
+
/**
* Implemented from the AutoCloseable interface.
*/
@Override
public void close() throws ExecutionException, InterruptedException {
- if (ccapDataChangeListenerRegistration != null) {
- ccapDataChangeListenerRegistration.close();
+ if (ccapsDataChangeListenerRegistration != null) {
+ ccapsDataChangeListenerRegistration.close();
}
if (qosDataChangeListenerRegistration != null) {
}
}
- public InetAddress getInetAddress(final String subId){
- try {
- return InetAddress.getByName(subId);
- } catch (UnknownHostException e) {
- logger.error("getInetAddress: {} FAILED: {}", subId, e.getMessage());
- return null;
- }
- }
-
- private String getIpPrefixStr(final IpPrefix ipPrefix) {
- final Ipv4Prefix ipv4 = ipPrefix.getIpv4Prefix();
- if (ipv4 != null) {
- return ipv4.getValue();
- } else {
- return ipPrefix.getIpv6Prefix().getValue();
- }
- }
-
private void updateCcapMaps(final Ccap ccap) {
// add ccap to the subscriberSubnets map
for (final IpPrefix ipPrefix : ccap.getSubscriberSubnets()) {
}
}
- private void removeCcapFromAllMaps(final Ccap ccap) {
- // remove the ccap from all maps
- // subscriberSubnets map
- for (final Map.Entry<Subnet, Ccap> entry : subscriberSubnetsMap.entrySet()) {
- if (entry.getValue() == ccap) {
- subscriberSubnetsMap.remove(entry.getKey());
- }
- }
- // ccap to upstream SCN map
- for (final Map.Entry<ServiceClassName, List<Ccap>> entry : upstreamScnMap.entrySet()) {
- final List<Ccap> ccapList = entry.getValue();
- ccapList.remove(ccap);
- if (ccapList.isEmpty()) {
- upstreamScnMap.remove(entry.getKey());
- }
- }
- // ccap to downstream SCN map
- for (final Map.Entry<ServiceClassName, List<Ccap>> entry : downstreamScnMap.entrySet()) {
- final List<Ccap> ccapList = entry.getValue();
- ccapList.remove(ccap);
- if (ccapList.isEmpty()) {
- downstreamScnMap.remove(entry.getKey());
- }
+ private String getIpPrefixStr(final IpPrefix ipPrefix) {
+ final Ipv4Prefix ipv4 = ipPrefix.getIpv4Prefix();
+ if (ipv4 != null) {
+ return ipv4.getValue();
+ } else {
+ return ipPrefix.getIpv6Prefix().getValue();
}
+ }
- final PCMMService service = pcmmServiceMap.remove(ccap.getCcapId());
- if (service != null) service.disconect();
+ public InetAddress getInetAddress(final String subId) {
+ try {
+ return InetAddress.getByName(subId);
+ } catch (UnknownHostException e) {
+ logger.error("getInetAddress: {} FAILED: {}", subId, e.getMessage());
+ return null;
+ }
}
private Ccap findCcapForSubscriberId(final InetAddress inetAddr) {
}
private ServiceFlowDirection findScnOnCcap(final ServiceClassName scn, final Ccap ccap) {
+ checkNotNull(scn);
+ checkNotNull(ccap);
+
if (upstreamScnMap.containsKey(scn)) {
final List<Ccap> ccapList = upstreamScnMap.get(scn);
if (ccapList.contains(ccap)) {
return null;
}
+ private void removeCcapFromAllMaps(final Ccap ccap) {
+ // remove the ccap from all maps
+ // subscriberSubnets map
+ for (final Map.Entry<Subnet, Ccap> entry : subscriberSubnetsMap.entrySet()) {
+ if (entry.getValue() == ccap) {
+ subscriberSubnetsMap.remove(entry.getKey());
+ }
+ }
+ // ccap to upstream SCN map
+ for (final Map.Entry<ServiceClassName, List<Ccap>> entry : upstreamScnMap.entrySet()) {
+ final List<Ccap> ccapList = entry.getValue();
+ ccapList.remove(ccap);
+ if (ccapList.isEmpty()) {
+ upstreamScnMap.remove(entry.getKey());
+ }
+ }
+ // ccap to downstream SCN map
+ for (final Map.Entry<ServiceClassName, List<Ccap>> entry : downstreamScnMap.entrySet()) {
+ final List<Ccap> ccapList = entry.getValue();
+ ccapList.remove(ccap);
+ if (ccapList.isEmpty()) {
+ downstreamScnMap.remove(entry.getKey());
+ }
+ }
+
+ final PCMMService service = pcmmServiceMap.remove(ccap.getCcapId());
+ if (service != null) {
+ service.disconect();
+ }
+ }
+
+ // ValidationException does not need to be thrown again
+ @SuppressWarnings("ThrowableResultOfMethodCallIgnored")
+ private <T extends DataObject> void saveErrors(@Nonnull Map<InstanceIdentifier<T>, ValidationException> errorMap,
+ @Nonnull Map<InstanceIdentifier<T>, T> dataMap) {
+
+ final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction();
+
+
+ for (InstanceIdentifier<T> iid : errorMap.keySet()) {
+
+ final ValidationException exception = errorMap.get(iid);
+ final T badData = dataMap.get(iid);
+
+ if (!badData.getImplementedInterface().isAssignableFrom(iid.getTargetType())) {
+ // InstanceIdentifier<T> does not have the same type as the DataObject
+ logger.error("Bad InstanceIdentifier to DataObject mapping, {} : {}", iid, badData);
+ continue;
+ }
+
+ if (badData instanceof Ccap) {
+ final Ccap ccap = (Ccap) badData;
+
+ final Ccap opperationalCcap =
+ new CcapBuilder().setCcapId(ccap.getCcapId()).setError(exception.getErrorMessages()).build();
+
+
+ // type match between iid and badData is done at start of loop
+ @SuppressWarnings("unchecked")
+ final InstanceIdentifier<Ccap> ccapIID = (InstanceIdentifier<Ccap>) iid;
+ writeTransaction.put(LogicalDatastoreType.OPERATIONAL, ccapIID, opperationalCcap);
+ }
+ else if (badData instanceof Gate) {
+ final Gate gate = (Gate) badData;
+
+ final Gate operationalGate =
+ new GateBuilder()
+ .setGateId(gate.getGateId())
+ .setError(exception.getErrorMessages())
+ .build();
+
+ final Gates operationalGates = new GatesBuilder()
+ .setGate(Collections.singletonList(operationalGate))
+ .build();
+
+ final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(iid.firstIdentifierOf(Subscriber.class));
+ final Subscriber operationalSubscriber = new SubscriberBuilder()
+ .setSubscriberId(subscriberKey.getSubscriberId())
+ .setGates(operationalGates)
+ .build();
+
+ final Subscribers operationalSubscribers = new SubscribersBuilder()
+ .setSubscriber(Collections.singletonList(operationalSubscriber))
+ .build();
+
+ final InstanceIdentifier<App> appIID = iid.firstIdentifierOf(App.class);
+ final AppKey appKey = InstanceIdentifier.keyOf(appIID);
+ final App operationalApp = new AppBuilder()
+ .setAppId(appKey.getAppId())
+ .setSubscribers(operationalSubscribers)
+ .build();
+
+
+ writeTransaction.put(LogicalDatastoreType.OPERATIONAL, appIID, operationalApp);
+ }
+ else {
+ // If you get here a developer forgot to add a type above
+ logger.error("Unexpected type requested for error saving: {}", badData);
+ throw new IllegalStateException("Unsupported type for error saving");
+ }
+
+ }
+
+
+ CheckedFuture<Void, TransactionCommitFailedException> future = writeTransaction.submit();
+
+ try {
+ future.checkedGet();
+ } catch (TransactionCommitFailedException e) {
+ logger.error("Failed to write errors to operational datastore", e);
+ }
+ }
+
/**
- * Implemented from the DataChangeListener interface.
+ * Removes Ccaps if all Ccap instances are removed
*/
+ private class CcapsCleaner extends AbstractCleaner<Ccaps> {
- private class InstanceData {
- // CCAP Identity
- public final Map<InstanceIdentifier<Ccap>, Ccap> ccapIidMap = new HashMap<>();
- // Gate Identity
- public String subId;
- public final Map<String, String> gatePathMap = new HashMap<>();
- public String gatePath;
- public final Map<InstanceIdentifier<Gate>, Gate> gateIidMap = new HashMap<>();
- // remove path for either CCAP or Gate
- public final Set<String> removePathList = new HashSet<>();
-
- public final Set<InstanceIdentifier<?>> reqCcapIds = new HashSet<>();
-
- public InstanceData(final Map<InstanceIdentifier<?>, DataObject> thisData) {
- // only used to parse createdData or updatedData
- getCcaps(thisData);
- if (ccapIidMap.isEmpty()) {
- getGates(thisData);
- if (! gateIidMap.isEmpty()){
- gatePath = gatePathMap.get("appId") + "/" + gatePathMap.get("subId");
- }
- }
+ public CcapsCleaner(final InstanceIdentifier<?> removedIID) {
+ super(removedIID, Ccaps.class, LogicalDatastoreType.OPERATIONAL);
}
- public InstanceData(final Set<InstanceIdentifier<?>> thisData) {
- // only used to parse the removedData paths
- for (final InstanceIdentifier<?> removeThis : thisData) {
- getGatePathMap(removeThis);
- if (gatePathMap.containsKey("ccapId")) {
- gatePath = gatePathMap.get("ccapId");
- removePathList.add(gatePath);
- } else if (gatePathMap.containsKey("gateId")) {
- gatePath = gatePathMap.get("appId") + "/" + gatePathMap.get("subId") + "/" + gatePathMap.get("gateId");
- removePathList.add(gatePath);
- }
- }
+ @Override
+ protected boolean shouldClean(final Ccaps ccaps) {
+ return ccaps.getCcap().isEmpty();
}
- private void getGatePathMap(final InstanceIdentifier<?> thisInstance) {
- logger.info("onDataChanged().getGatePathMap(): " + thisInstance);
- try {
- final InstanceIdentifier<Ccap> ccapInstance = thisInstance.firstIdentifierOf(Ccap.class);
- if (ccapInstance != null) {
- final CcapKey ccapKey = InstanceIdentifier.keyOf(ccapInstance);
- if (ccapKey != null) {
- gatePathMap.put("ccapId", ccapKey.getCcapId());
- }
- } else {
- // get the gate path keys from the InstanceIdentifier Map key set if they are there
- final InstanceIdentifier<App> appInstance = thisInstance.firstIdentifierOf(App.class);
- if (appInstance != null) {
- final AppKey appKey = InstanceIdentifier.keyOf(appInstance);
- if (appKey != null) {
- gatePathMap.put("appId", appKey.getAppId());
- }
- }
- final InstanceIdentifier<Subscriber> subInstance = thisInstance.firstIdentifierOf(Subscriber.class);
- if (subInstance != null) {
- final SubscriberKey subKey = InstanceIdentifier.keyOf(subInstance);
- if (subKey != null) {
- subId = subKey.getSubscriberId();
- gatePathMap.put("subId", subId);
+ }
+
+ /**
+ * Removes Subscriber if all Gate instances are removed
+ */
+ private class SubscriberCleaner extends AbstractCleaner<Subscriber> {
+
+ public SubscriberCleaner(InstanceIdentifier<Gate> removedGateIID) {
+ super(removedGateIID, Subscriber.class, LogicalDatastoreType.OPERATIONAL);
+ }
+
+ @Override
+ protected boolean shouldClean(final Subscriber subscriber) {
+ return subscriber.getGates().getGate().isEmpty();
+ }
+
+ @Override
+ protected void postRemove(InstanceIdentifier<Subscriber> subscriberIID) {
+ executor.execute(new AppCleaner(subscriberIID));
+ }
+ }
+
+
+ /**
+ * Removes App if all Subscribers are removed.
+ */
+ private class AppCleaner extends AbstractCleaner<App> {
+
+ public AppCleaner(InstanceIdentifier<Subscriber> removedSubscriberIID) {
+ super(removedSubscriberIID, App.class, LogicalDatastoreType.OPERATIONAL);
+ }
+
+ @Override
+ boolean shouldClean(final App app) {
+ return app.getSubscribers().getSubscriber().isEmpty();
+ }
+
+ @Override
+ void postRemove(final InstanceIdentifier<App> appIID) {
+ executor.execute(new AppsCleaner(appIID));
+ }
+ }
+
+
+ /**
+ * Removes Apps if all App instances are removed.
+ */
+ private class AppsCleaner extends AbstractCleaner<Apps> {
+
+ public AppsCleaner(InstanceIdentifier<App> removedAppIID) {
+ super(removedAppIID, Apps.class, LogicalDatastoreType.OPERATIONAL);
+ }
+
+ @Override
+ protected boolean shouldClean(final Apps apps) {
+ return apps.getApp().isEmpty();
+ }
+ }
+
+
+ /**
+ * Helper class to do the heavy lifting in removing object. Lets subclasses decide with
+ * {@link #shouldClean(DataObject)}. <br>
+ *
+ * Subclasses can react after an instance is removed by overriding {@link #postRemove(InstanceIdentifier)}
+ * @param <T> The type that will be removed
+ */
+ private abstract class AbstractCleaner <T extends DataObject> implements Runnable {
+ final InstanceIdentifier<?> removedIID;
+ final Class<T> tClass;
+ final LogicalDatastoreType datastoreType;
+
+ public AbstractCleaner(InstanceIdentifier<?> removedIID, Class<T> tClass, LogicalDatastoreType datastoreType) {
+ this.removedIID = checkNotNull(removedIID);
+ this.tClass = checkNotNull(tClass);
+ this.datastoreType = checkNotNull(datastoreType);
+ }
+
+ @Override
+ public void run() {
+ InstanceIdentifier<T> tIID = removedIID.firstIdentifierOf(tClass);
+ if (tIID != null) {
+ Optional<T> optional = mdsalUtils.read(datastoreType, tIID);
+ if (optional.isPresent()) {
+
+ if (shouldClean(optional.get())) {
+ if (mdsalUtils.delete(datastoreType, tIID)) {
+ postRemove(tIID);
}
- }
- final InstanceIdentifier<Gate> gatesInstance = thisInstance.firstIdentifierOf(Gate.class);
- if (gatesInstance != null) {
- final GateKey gateKey = InstanceIdentifier.keyOf(gatesInstance);
- if (gateKey != null) {
- gatePathMap.put("gateId", gateKey.getGateId());
+ else {
+ removeFailed(tIID);
}
}
+
}
- } catch (ClassCastException err) {
- logger.warn("Unexpected exception", err);
+ }
+ else {
+ logger.error("Expected to find InstanceIdentifier<{}> but was not found: {}",
+ tClass.getSimpleName(), removedIID);
}
}
- private void getCcaps(final Map<InstanceIdentifier<?>, DataObject> thisData) {
- logger.info("onDataChanged().getCcaps(): " + thisData);
- for (final Map.Entry<InstanceIdentifier<?>, DataObject> entry : thisData.entrySet()) {
+ /**
+ * If returns true the object will be removed from the datastore
+ * @param object The object that might be removed.
+ * @return true if it should be removed.
+ */
+ abstract boolean shouldClean(final T object);
- if (entry.getKey().getTargetType().equals(Ccap.class)) {
- Ccap ccaps = ((Ccap) entry.getValue());
- InstanceIdentifier<Ccap> ccapsIid = InstanceIdentifier.builder(Ccaps.class).child(Ccap.class, new CcapKey(ccaps.getCcapId())).build();
- ccapIidMap.put(ccapsIid, ccaps);
- }
+ /**
+ * Called after an instance is removed.
+ * @param tIID the InstanceIdentifier of the removed object
+ */
+ void postRemove(InstanceIdentifier<T> tIID) {
- if (entry.getKey().getTargetType().equals(Connection.class) ||
- entry.getKey().getTargetType().equals(Ccap.class)) {
- reqCcapIds.add(entry.getKey());
- }
- }
}
- private void getGates(final Map<InstanceIdentifier<?>, DataObject> thisData) {
- logger.info("onDataChanged().getGates(): " + thisData);
- for (final Map.Entry<InstanceIdentifier<?>, DataObject> entry : thisData.entrySet()) {
- if (entry.getValue() instanceof Gate) {
- final Gate gate = (Gate)entry.getValue();
-
- // TODO FIXME - Potential ClassCastException thrown here!!!
- final InstanceIdentifier<Gate> gateIID = (InstanceIdentifier<Gate>)entry.getKey();
- getGatePathMap(gateIID);
- if (!gateIidMap.containsKey(gateIID)){
- gateIidMap.put(gateIID, gate);
- }
- }
- // TODO reconciliate gates
-// if (entry.getValue() instanceof Qos) {
-// final Qos qos = (Qos) entry.getValue();
-// if (qos.getApps() != null) {
-// for (Apps apps : qos.getApps()) {
-// if (apps.getSubs() != null) {
-// for (Subs subs : apps.getSubs()) {
-// if (subs.getGates() != null) {
-// for (Gate gates : subs.getGates()) {
-// final InstanceIdentifier<Gate> gateIID = (InstanceIdentifier<Gate>)entry.getKey();
-// getGatePathMap(gateIID);
-// if (!gateIidMap.containsKey(gateIID)){
-// gateIidMap.put(gateIID, gates);
-// }
-// }
-// }
-// }
-// }
-// }
-// }
-// }
- }
+ void removeFailed(InstanceIdentifier<T> tIID) {
+ logger.error("Failed to remove {}", tIID);
}
}
- @Override
- public void onDataChanged(final AsyncDataChangeEvent<InstanceIdentifier<?>, DataObject> change) {
- logger.info("onDataChanged");
- // Determine what change action took place by looking at the change object's InstanceIdentifier sets
- // and validate all instance data
- if (!change.getCreatedData().isEmpty()) {
- if (!new ValidateInstanceData(mdsalUtils, change.getCreatedData()).validateYang()) {
- // leave now -- a bad yang object has been detected and a response object has been inserted
+
+ /**
+ * Listener for the packetcable:ccaps tree
+ */
+ private class CcapsDataChangeListener extends AbstractDataChangeListener<Ccap> {
+
+ private final DataValidator ccapsDataValidator = new DataValidator(new CcapsValidatorProviderFactory().build());
+
+ private final Set<InstanceIdentifier<Ccap>> updateQueue = Sets.newConcurrentHashSet();
+
+ public CcapsDataChangeListener() {
+ super(Ccap.class);
+ }
+
+ @Override
+ protected void handleCreatedData(final Map<InstanceIdentifier<Ccap>, Ccap> createdCcaps) {
+ if (createdCcaps.isEmpty()) {
return;
}
- onCreate(new InstanceData(change.getCreatedData()));
- } else if (!change.getRemovedPaths().isEmpty()) {
- onRemove(new InstanceData(change.getRemovedPaths()));
- } else if (!change.getUpdatedData().isEmpty()) {
- onUpdate(new InstanceData(change.getUpdatedData()));
- } else {
- // we should not be here -- complain bitterly and return
- logger.error("onDataChanged(): Unknown change action: " + change);
- }
- }
- private void onCreate(final InstanceData thisData) {
- logger.info("onCreate(): " + thisData);
+ final Map<InstanceIdentifier<Ccap>, ValidationException> errorMap =
+ ccapsDataValidator.validateOneType(createdCcaps, Validator.Extent.NODE_AND_SUBTREE);
+
+ // validate all new objects an update operational datastore
+ if (!errorMap.isEmpty()) {
+ // bad data write errors to operational datastore
+ saveErrors(errorMap, createdCcaps);
+ }
+
+ if (createdCcaps.size() > errorMap.size()) {
+ final Map<InstanceIdentifier<Ccap>, Ccap> goodData =
+ Maps.newHashMapWithExpectedSize(createdCcaps.size() - errorMap.size());
+ for (InstanceIdentifier<Ccap> iid : createdCcaps.keySet()) {
+ if (!errorMap.containsKey(iid)) {
+ goodData.put(iid, createdCcaps.get(iid));
+ }
+ }
+ addNewCcaps(goodData);
+ }
+ }
- // get the CCAP parameters
- String message;
- if (! thisData.reqCcapIds.isEmpty()) {
- for (Map.Entry<InstanceIdentifier<Ccap>, Ccap> entry : thisData.ccapIidMap.entrySet()) {
- final Ccap thisCcap = entry.getValue();
- // get the CCAP node identity from the Instance Data
- final String ccapId = thisCcap.getCcapId();
+ private void addNewCcaps(final Map<InstanceIdentifier<Ccap>, Ccap> goodData) {
+ for (InstanceIdentifier<Ccap> iid : goodData.keySet()) {
+ final Ccap ccap = goodData.get(iid);
- if (pcmmServiceMap.get(thisCcap.getCcapId()) == null) {
- final PCMMService pcmmService = new PCMMService(IPCMMClient.CLIENT_TYPE, thisCcap);
- // TODO - may want to use the AMID but for the client type but probably not???
+ // add service
+ if (pcmmServiceMap.containsKey(ccap.getCcapId())) {
+ logger.error("Already monitoring CCAP - " + ccap);
+ continue;
+ }
+ final PCMMService pcmmService = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
+ // TODO - may want to use the AMID but for the client type but probably not???
/*
final PCMMService pcmmService = new PCMMService(
thisCcap.getAmId().getAmType().shortValue(), thisCcap);
*/
- message = pcmmService.addCcap();
- if (message.contains("200 OK")) {
- pcmmServiceMap.put(thisCcap.getCcapId(), pcmmService);
- ccapMap.put(ccapId, thisCcap);
- updateCcapMaps(thisCcap);
- logger.info("Created CCAP: {}/{} : {}", thisData.gatePath, thisCcap, message);
- logger.info("Created CCAP: {} : {}", thisData.gatePath, message);
- } else {
- logger.error("Create CCAP Failed: {} : {}", thisData.gatePath, message);
- for (final InstanceIdentifier<?> instId : thisData.reqCcapIds) {
- mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, instId);
- }
- ccapMap.remove(ccapId);
- }
+ ConnectionBuilder connectionBuilder = new ConnectionBuilder();
+ String message = pcmmService.addCcap();
+ if (message.contains("200 OK")) {
+ pcmmServiceMap.put(ccap.getCcapId(), pcmmService);
+ ccapMap.put(ccap.getCcapId(), ccap);
+ updateCcapMaps(ccap);
+ logger.info("Created CCAP: {}/{} : {}", iid, ccap, message);
+ logger.info("Created CCAP: {} : {}", iid, message);
+ connectionBuilder.setConnected(true).setError(Collections.<String>emptyList());
} else {
- logger.error("Already monitoring CCAP - " + thisCcap);
- break;
+ logger.error("Create CCAP Failed: {} : {}", iid, message);
+
+ connectionBuilder.setConnected(false).setError(Collections.singletonList(message));
}
- }
- } else {
- // get the PCMM gate parameters from the ccapId/appId/subId/gateId path in the Maps entry (if new gate)
- for (final Map.Entry<InstanceIdentifier<Gate>, Gate> entry : thisData.gateIidMap.entrySet()) {
- message = null;
- final Gate gate = entry.getValue();
- final String gateId = gate.getGateId();
- final String gatePathStr = thisData.gatePath + "/" + gateId ;
- final InetAddress subId = getInetAddress(thisData.subId);
- if (subId != null) {
- final Ccap thisCcap = findCcapForSubscriberId(subId);
- if (thisCcap != null) {
- final String ccapId = thisCcap.getCcapId();
- // verify SCN exists on CCAP and force gateSpec.Direction to align with SCN direction
- final ServiceClassName scn = gate.getTrafficProfile().getServiceClassName();
- if (scn != null) {
- final ServiceFlowDirection scnDir = findScnOnCcap(scn, thisCcap);
- if (scnDir != null) {
- if (pcmmServiceMap.get(thisCcap.getCcapId()) != null) {
- message = pcmmServiceMap.get(thisCcap.getCcapId()).sendGateSet(gatePathStr, subId, gate, scnDir);
- gateMap.put(gatePathStr, gate);
- gateCcapMap.put(gatePathStr, thisCcap.getCcapId());
-
- if (message.contains("200 OK")) {
- logger.info("Created QoS gate {} for {}/{}/{} - {}",
- gateId, ccapId, gatePathStr, gate, message);
- logger.info("Created QoS gate {} for {}/{} - {}",
- gateId, ccapId, gatePathStr, message);
- } else {
- logger.info("Unable to create QoS gate {} for {}/{}/{} - {}",
- gateId, ccapId, gatePathStr, gate, message);
- logger.error("Unable to create QoS gate {} for {}/{} - {}",
- gateId, ccapId, gatePathStr, message);
- }
- } else {
- logger.error("Unable to locate PCMM Service for CCAP - " + thisCcap);
- break;
- }
- } else {
- logger.error("PCMMService: sendGateSet(): SCN {} not found on CCAP {} for {}/{}",
- scn.getValue(), thisCcap, gatePathStr, gate);
- message = String.format("404 Not Found - SCN %s not found on CCAP %s for %s",
- scn.getValue(), thisCcap.getCcapId(), gatePathStr);
- }
- }
- } else {
- final String subIdStr = thisData.subId;
- message = String.format("404 Not Found - no CCAP found for subscriber %s in %s",
- subIdStr, gatePathStr);
- logger.info("Create QoS gate {} FAILED: no CCAP found for subscriber {}: @ {}/{}",
- gateId, subIdStr, gatePathStr, gate);
- logger.error("Create QoS gate {} FAILED: no CCAP found for subscriber {}: @ {}",
- gateId, subIdStr, gatePathStr);
- }
+
+ Optional<Ccap> optionalCcap = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, iid);
+
+ final CcapBuilder responseCcapBuilder;
+ if (optionalCcap.isPresent()) {
+ responseCcapBuilder = new CcapBuilder(optionalCcap.get());
} else {
- final String subIdStr = thisData.subId;
- message = String.format("400 Bad Request - subId must be a valid IP address for subscriber %s in %s",
- subIdStr, gatePathStr);
- logger.info("Create QoS gate {} FAILED: subId must be a valid IP address for subscriber {}: @ {}/{}",
- gateId, subIdStr, gatePathStr, gate);
- logger.error("Create QoS gate {} FAILED: subId must be a valid IP address for subscriber {}: @ {}",
- gateId, subIdStr, gatePathStr);
+ responseCcapBuilder = new CcapBuilder();
+ responseCcapBuilder.setCcapId(ccap.getCcapId());
}
- if (!message.contains("200 OK")) {
- mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, entry.getKey());
+
+ responseCcapBuilder.setConnection(connectionBuilder.build());
+
+ mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, iid, responseCcapBuilder.build());
+ }
+
+ }
+
+ @Override
+ protected void handleUpdatedData(final Map<InstanceIdentifier<Ccap>, Ccap> updatedCcaps,
+ final Map<InstanceIdentifier<Ccap>, Ccap> originalCcaps) {
+
+ // TODO actually support updates
+
+ // update operation not allowed -- restore the original config object and complain
+ for (final Map.Entry<InstanceIdentifier<Ccap>, Ccap> entry : updatedCcaps.entrySet()) {
+ if (!originalCcaps.containsKey(entry.getKey())) {
+ logger.error("No original data found for supposedly updated data: {}", entry.getValue());
+ continue;
}
+
+ // If this notification is coming from our modification ignore it.
+ if (updateQueue.contains(entry.getKey())) {
+ updateQueue.remove(entry.getKey());
+ continue;
+ }
+
+ final Ccap originalCcap = originalCcaps.get(entry.getKey());
+ //final Ccap updatedCcap = entry.getValue();
+
+ // restore the original data
+ updateQueue.add(entry.getKey());
+ mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, entry.getKey(), originalCcap);
+ logger.error("CCAP update not permitted {}", entry.getKey());
}
}
+
+ @Override
+ protected void handleRemovedData(final Set<InstanceIdentifier<Ccap>> removedCcapPaths,
+ final Map<InstanceIdentifier<Ccap>, Ccap> originalCcaps) {
+
+ for (InstanceIdentifier<Ccap> iid : removedCcapPaths) {
+ final Ccap nukedCcap = originalCcaps.get(iid);
+ removeCcapFromAllMaps(nukedCcap);
+
+ mdsalUtils.delete(LogicalDatastoreType.OPERATIONAL, iid);
+
+ // clean up ccaps level if it is now empty
+ executor.execute(new CcapsCleaner(iid));
+ }
+
+ }
}
- private void onRemove(final InstanceData thisData) {
- logger.info("onRemove(): " + thisData);
- for (final String gatePathStr: thisData.removePathList) {
- if (gateMap.containsKey(gatePathStr)) {
- final Gate thisGate = gateMap.remove(gatePathStr);
- final String gateId = thisGate.getGateId();
- final String ccapId = gateCcapMap.remove(gatePathStr);
- final Ccap thisCcap = ccapMap.get(ccapId);
- final PCMMService service = pcmmServiceMap.get(thisCcap.getCcapId());
- if (service != null) {
- service.sendGateDelete(gatePathStr);
- logger.info("onDataChanged(): removed QoS gate {} for {}/{}/{}: ", gateId, ccapId, gatePathStr, thisGate);
- logger.info("onDataChanged(): removed QoS gate {} for {}/{}: ", gateId, ccapId, gatePathStr);
- } else
- logger.warn("Unable to send to locate PCMMService to send gate delete message with CCAP - "
- + thisCcap);
+
+ private class QosDataChangeListener extends AbstractDataChangeListener<Gate> {
+
+ private final DataValidator qosDataValidator = new DataValidator(new QosValidatorProviderFactory().build());
+ private final Set<InstanceIdentifier<Gate>> updateQueue = Sets.newConcurrentHashSet();
+
+ public QosDataChangeListener() {
+ super(Gate.class);
+ }
+
+ @Override
+ protected void handleCreatedData(final Map<InstanceIdentifier<Gate>, Gate> createdData) {
+
+ final Map<InstanceIdentifier<Gate>, ValidationException> errorMap =
+ qosDataValidator.validateOneType(createdData, Validator.Extent.NODE_AND_SUBTREE);
+
+ // validate all new objects an update operational datastore
+ if (!errorMap.isEmpty()) {
+ // bad data write errors to operational datastore
+ saveErrors(errorMap, createdData);
+ }
+
+ if (createdData.size() > errorMap.size()) {
+ final Map<InstanceIdentifier<Gate>, Gate> goodData =
+ Maps.newHashMapWithExpectedSize(createdData.size() - errorMap.size());
+ for (InstanceIdentifier<Gate> iid : createdData.keySet()) {
+ if (!errorMap.containsKey(iid)) {
+ goodData.put(iid, createdData.get(iid));
+ }
+ }
+ addNewGates(goodData);
}
+
}
- for (final String ccapIdStr: thisData.removePathList) {
- if (ccapMap.containsKey(ccapIdStr)) {
- final Ccap thisCcap = ccapMap.remove(ccapIdStr);
- removeCcapFromAllMaps(thisCcap);
+
+ private void addNewGates(final Map<InstanceIdentifier<Gate>, Gate> createdGates) {
+
+ for (InstanceIdentifier<Gate> gateIID : createdGates.keySet()) {
+ final Gate newGate = createdGates.get(gateIID);
+
+ final String newGatePathStr = makeGatePathString(gateIID);
+
+ final InstanceIdentifier<Subscriber> subscriberIID = gateIID.firstIdentifierOf(Subscriber.class);
+ final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(subscriberIID);
+ final InetAddress subscriberAddr = getInetAddress(subscriberKey.getSubscriberId());
+ if (subscriberAddr == null) {
+ final String msg = String.format("subscriberId must be a valid ipaddress: %s",
+ subscriberKey.getSubscriberId());
+ logger.error(msg);
+ saveGateError(gateIID, newGatePathStr, msg);
+ continue;
+ }
+
+ final Ccap ccap = findCcapForSubscriberId(subscriberAddr);
+ if (ccap == null) {
+ final String msg = String.format("Unable to find Ccap for subscriber %s: @ %s",
+ subscriberKey.getSubscriberId(), newGatePathStr);
+ logger.error(msg);
+ saveGateError(gateIID, newGatePathStr, msg);
+ continue;
+ }
+
+ final ServiceClassName scn = newGate.getTrafficProfile().getServiceClassName();
+ final ServiceFlowDirection scnDirection = findScnOnCcap(scn, ccap);
+ if (scnDirection == null) {
+ final String msg = String.format("SCN %s not found on CCAP %s for %s",
+ scn, ccap.getCcapId(), newGatePathStr);
+ logger.error(msg);
+ saveGateError(gateIID, newGatePathStr, msg);
+ continue;
+ }
+
+ final PCMMService pcmmService = pcmmServiceMap.get(ccap.getCcapId());
+ if (pcmmService == null) {
+ final String msg = String.format("Unable to locate PCMM Service for CCAP: %s ; with subscriber: %s",
+ ccap, subscriberKey.getSubscriberId());
+ logger.error(msg);
+ saveGateError(gateIID, newGatePathStr, msg);
+ continue;
+ }
+
+ PCMMService.GateSetStatus status = pcmmService.sendGateSet(newGatePathStr, subscriberAddr, newGate, scnDirection);
+ gateMap.put(newGatePathStr, newGate);
+ gateCcapMap.put(newGatePathStr, ccap.getCcapId());
+
+ final GateBuilder gateBuilder = new GateBuilder();
+ gateBuilder.setGateId(newGate.getGateId())
+ .setGatePath(newGatePathStr)
+ .setCcapId(ccap.getCcapId())
+ .setCopsGateId(status.getCopsGateId())
+ .setCopsState(status.didSucceed() ? "success" : "failure");
+ if (!status.didSucceed()) {
+ gateBuilder.setError(Collections.singletonList(status.getMessage()));
+ }
+
+ Gate operationalGate = gateBuilder.build();
+
+ mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIID, operationalGate);
+
}
+
}
- }
- private void onUpdate(final InstanceData oldData) {
- logger.info("onUpdate(): " + oldData);
- // update operation not allowed -- restore the original config object and complain
- if (! oldData.ccapIidMap.isEmpty()) {
- for (final Map.Entry<InstanceIdentifier<Ccap>, Ccap> entry : oldData.ccapIidMap.entrySet()) {
- final Ccap ccap = entry.getValue();
- final String ccapId = ccap.getCcapId();
- // restores the original data - although I don't think this is what is done here! I think the update data is put into the DS/config
- mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, entry.getKey(), ccap);
- logger.error("onDataChanged(): CCAP update not permitted {}/{}", ccapId, ccap);
+ private void saveGateError(@Nonnull final InstanceIdentifier<Gate> gateIID, @Nonnull final String gatePathStr,
+ @Nonnull final String error) {
+ checkNotNull(gateIID);
+ checkNotNull(error);
+
+ final GateBuilder gateBuilder = new GateBuilder();
+ gateBuilder.setGateId(InstanceIdentifier.keyOf(gateIID).getGateId())
+ .setGatePath(gatePathStr)
+ .setCopsGateId("")
+ .setCopsState("N/A");
+
+ gateBuilder.setError(Collections.singletonList(error));
+
+ Gate operationalGate = gateBuilder.build();
+
+ mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIID, operationalGate);
+ }
+
+ @Override
+ protected void handleUpdatedData(final Map<InstanceIdentifier<Gate>, Gate> updatedData,
+ final Map<InstanceIdentifier<Gate>, Gate> originalData) {
+ // TODO actually support updates
+
+ // update operation not allowed -- restore the original config object and complain
+ for (final Map.Entry<InstanceIdentifier<Gate>, Gate> entry : updatedData.entrySet()) {
+ if (!originalData.containsKey(entry.getKey())) {
+ logger.error("No original data found for supposedly updated data: {}", entry.getValue());
+ continue;
+ }
+
+ // If this notification is coming from our modification ignore it.
+ if (updateQueue.contains(entry.getKey())) {
+ updateQueue.remove(entry.getKey());
+ continue;
+ }
+
+ final Gate originalGate = originalData.get(entry.getKey());
+
+ // restores the original data
+ updateQueue.add(entry.getKey());
+ mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, entry.getKey(), originalGate);
+ logger.error("Update not permitted {}", entry.getKey());
+
}
- } else {
- for (final Map.Entry<InstanceIdentifier<Gate>, Gate> entry : oldData.gateIidMap.entrySet()) {
- final Gate gate = entry.getValue();
- final String gatePathStr = oldData.gatePath + "/" + gate.getGateId() ;
- // restores the original data - although I don't think this is what is done here! I think the update data is put into the DS/config
- mdsalUtils.merge(LogicalDatastoreType.CONFIGURATION, entry.getKey(), gate);
- logger.error("onDataChanged(): QoS Gate update not permitted: {}/{}", gatePathStr, gate);
+ }
+
+
+
+ @Override
+ protected void handleRemovedData(final Set<InstanceIdentifier<Gate>> removedPaths,
+ final Map<InstanceIdentifier<Gate>, Gate> originalData) {
+
+ for (final InstanceIdentifier<Gate> removedGateIID : removedPaths) {
+
+ mdsalUtils.delete(LogicalDatastoreType.OPERATIONAL, removedGateIID);
+ //TODO check if this was the last gate for this app/subscriber and if so delete them
+
+ executor.execute(new SubscriberCleaner(removedGateIID));
+
+ final String gatePathStr = makeGatePathString(removedGateIID);
+
+ if (gateMap.containsKey(gatePathStr)) {
+ final Gate thisGate = gateMap.remove(gatePathStr);
+ final String gateId = thisGate.getGateId();
+ final String ccapId = gateCcapMap.remove(gatePathStr);
+ final Ccap thisCcap = ccapMap.get(ccapId);
+ final PCMMService service = pcmmServiceMap.get(thisCcap.getCcapId());
+ if (service != null) {
+ service.sendGateDelete(gatePathStr);
+ logger.info("onDataChanged(): removed QoS gate {} for {}/{}/{}: ", gateId, ccapId, gatePathStr,
+ thisGate);
+ } else {
+ logger.warn(
+ "Unable to send to locate PCMMService to send gate delete message with CCAP - " + thisCcap);
+ }
+ }
+
+
}
+
+ }
+
+ private String makeGatePathString(InstanceIdentifier<Gate> iid) {
+ final InstanceIdentifier<App> appIID = iid.firstIdentifierOf(App.class);
+ final AppKey appKey = InstanceIdentifier.keyOf(appIID);
+
+ final InstanceIdentifier<Subscriber> subscriberIID = iid.firstIdentifierOf(Subscriber.class);
+ final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(subscriberIID);
+
+ final GateKey gateKey = InstanceIdentifier.keyOf(iid);
+
+ return appKey.getAppId()
+ + "/" + subscriberKey.getSubscriberId()
+ + "/" + gateKey.getGateId();
}
}
+
}
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
package org.opendaylight.controller.packetcable.provider;
-import org.apache.commons.lang3.builder.HashCodeBuilder;
-import javax.annotation.Nonnull;
-import javax.annotation.Nullable;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
import java.math.BigInteger;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
-
-import static com.google.common.base.Preconditions.checkArgument;
-import static com.google.common.base.Preconditions.checkNotNull;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import org.apache.commons.lang3.builder.HashCodeBuilder;
/**
* @author c3oe.de, based on snippets from Scott Plante, John Kugelmann
public class Subnet
{
/** Minimum length of a v4 or v6 subnet mask */
- private final static int MIN_MASK_BITS = 0;
+ private static final int MIN_MASK_BITS = 0;
/** Maximum length of a v4 subnet mask */
- private final static int MAX_MASK_BITS_V4 = 32;
+ private static final int MAX_MASK_BITS_V4 = 32;
/** Maximum length of a v6 subnet mask */
- private final static int MAX_MASK_BITS_V6 = 128;
+ private static final int MAX_MASK_BITS_V6 = 128;
/** The length of the subnet prefix */
private final int prefixLen;
}
@Override
- final public boolean equals( Object obj )
+ public final boolean equals( Object obj )
{
if (null == obj) return false;
if (this == obj) return true;
}
@Override
- final public int hashCode()
+ public final int hashCode()
{
return new HashCodeBuilder(997, 311)
.append(prefixLen)
return buf.toString();
}
- static private void bigInteger2IpString( final StringBuilder buf, final BigInteger bigInteger, final int displayBytes )
+ private static void bigInteger2IpString( final StringBuilder buf, final BigInteger bigInteger, final int displayBytes )
{
final boolean isIPv4 = 4 == displayBytes;
byte[] bytes = bigInteger.toByteArray();
buf.append( isIPv4 ? integer : Integer.toHexString( integer ) );
}
}
-}
\ No newline at end of file
+}
+++ /dev/null
-/**
- * Validate all instance data received from the config datastore via the onDataChange() notification.
- *
- * N.B. that yang typedefs are not validated when a PUT operation places them into the config datastore.
- * This means that they can arrive at onDataChange() with invalid values.
- *
- * In particular integer range values and string patterns (such as IP prefix/len) are not checked
- * and accessing these values via any object.getValue() method call will cause an exception (as yang
- * finally gets around to actually enforcing the typedef).
- */
-package org.opendaylight.controller.packetcable.provider;
-
-import java.lang.reflect.Method;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentHashMap;
-import org.opendaylight.controller.md.sal.common.api.data.LogicalDatastoreType;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv6Prefix;
-import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ServiceClassName;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ServiceFlowDirection;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.TosByte;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.TpProtocol;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.AmId;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.AmIdBuilder;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.Connection;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.ConnectionBuilder;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.Ccap;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.CcapBuilder;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.classifier.Classifier;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.classifier.ClassifierBuilder;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.ext.classifier.ExtClassifier;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.ext.classifier.ExtClassifierBuilder;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gate.spec.GateSpec;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gate.spec.GateSpecBuilder;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.GateBuilder;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.ipv6.classifier.Ipv6Classifier;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.ipv6.classifier.Ipv6ClassifierBuilder;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.traffic.profile.TrafficProfile;
-import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.traffic.profile.TrafficProfileBuilder;
-import org.opendaylight.yangtools.yang.binding.DataObject;
-import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-
-public class ValidateInstanceData {
-
- private final static Logger logger = LoggerFactory.getLogger(ValidateInstanceData.class);
-
- // Final members
- private final MdsalUtils mdsalUtils;
-
- // Gate Identities
- private final Map<InstanceIdentifier<Gate>, Gate> gateIidMap;
-
- // CCAP Identity
- private transient Ccap ccap;
- private transient InstanceIdentifier<Ccap> ccapIID;
-
- public ValidateInstanceData(final MdsalUtils mdsalUtils, final Map<InstanceIdentifier<?>, DataObject> thisData) {
- this.mdsalUtils = mdsalUtils;
- getCcap(thisData);
-
- // Must be instantiated prior to retreiving the gates below
- gateIidMap = new ConcurrentHashMap<>();
-
- // TODO FIXME - this value is always null???
- if (ccap == null) {
- getGates(thisData);
- }
- }
-
- public boolean validateYang() {
- if (ccap != null) {
- if (! validateCcap(ccap)) {
- logger.error("Validate CCAP {} failed - {}", ccap.getCcapId());
- mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, ccapIID);
- return false;
- }
- } else if (! gateIidMap.isEmpty()) {
- for (Map.Entry<InstanceIdentifier<Gate>, Gate> entry : gateIidMap.entrySet()) {
- InstanceIdentifier<Gate> gateIID = entry.getKey();
- Gate gate = entry.getValue();
- if (! validateGate(gate)) {
- logger.error("Validate Gate {} failed - {}", gate.getGateId());
- mdsalUtils.delete(LogicalDatastoreType.CONFIGURATION, gateIID);
- return false;
- }
- }
- }
- return true;
- }
-
- private void getCcap(final Map<InstanceIdentifier<?>, DataObject> thisData) {
- for (final Map.Entry<InstanceIdentifier<?>, DataObject> entry : thisData.entrySet()) {
- if (entry.getValue() instanceof Ccap) {
- ccap = (Ccap)entry.getValue();
- // TODO FIXME - ClassCastException waiting to occur here!!!
- ccapIID = (InstanceIdentifier<Ccap>) entry.getKey();
- }
- }
- }
-
- private void getGates(final Map<InstanceIdentifier<?>, DataObject> thisData) {
- for (final Map.Entry<InstanceIdentifier<?>, DataObject> entry : thisData.entrySet()) {
- if (entry.getValue() instanceof Gate) {
- final Gate gate = (Gate)entry.getValue();
- // TODO FIXME - ClassCastException waiting to occur here!!!
- final InstanceIdentifier<Gate> gateIID = (InstanceIdentifier<Gate>)entry.getKey();
- gateIidMap.put(gateIID, gate);
- }
- }
- }
- private String validateMethod(final Class<?> thisClass, final Object thisObj, final String methodName) {
- try {
- final Method method = thisClass.getMethod(methodName);
- method.invoke(thisObj);
- } catch (IllegalArgumentException e) {
- return e.getMessage();
- } catch (Exception e) {
- return " ";
-// error = String.format("%s.%s(): Method failed: %s ", thisClass.getSimpleName(), methodName, e.getMessage());
- }
- return null;
- }
-
- private boolean validateGateSpec(final Gate gate, final GateBuilder gateBuilder) {
- // gate-spec
- String message = "";
- String error;
- boolean valid = true;
- GateSpec gateSpec = gate.getGateSpec();
- if (gateSpec != null) {
- final ServiceFlowDirection dir;
- error = validateMethod(GateSpec.class, gateSpec, "getDirection");
- if (error == null) {
- dir = gateSpec.getDirection();
- if (dir != null) {
- if (gate.getTrafficProfile().getServiceClassName() != null) {
- message += " gate-spec.direction not allowed for traffic-profile.SCN;";
- valid = false;
- }
- }
- } else {
- message += " gate-spec.direction invalid: must be 'us' or 'ds' -" + error;
- dir = null;
- valid = false;
- }
- final TosByte tosByte;
- error = validateMethod(GateSpec.class, gateSpec, "getDscpTosOverwrite");
- if (error == null) {
- tosByte = gateSpec.getDscpTosOverwrite();
- } else {
- message += " gate-spec.dscp-tos-overwrite invalid: " + error;
- tosByte = null;
- valid = false;
- }
- final TosByte tosMask;
- error = validateMethod(GateSpec.class, gateSpec, "getDscpTosMask");
- if (error == null) {
- tosMask = gateSpec.getDscpTosMask();
- if (tosByte != null && tosMask == null) {
- message += " gate-spec.dscp-tos-mask missing;";
- valid = false;
- }
- } else {
- message += " gate-spec.dscp-tos-mask invalid: " + error;
- tosMask = null;
- valid = false;
- }
- if (! valid) {
- // rebuild the gateSpec with nulls replacing bad values
- final GateSpecBuilder gateSpecBuilder = new GateSpecBuilder();
- gateSpecBuilder.setDirection(dir);
- gateSpecBuilder.setDscpTosOverwrite(tosByte);
- gateSpecBuilder.setDscpTosMask(tosMask);
- gateSpec = gateSpecBuilder.build();
- // update the gate
- gateBuilder.setGateSpec(gateSpec);
- }
- }
- if (! valid) {
- logger.error(message);
- }
- return valid;
- }
-
- private boolean validateTrafficProfile(final Gate gate, final GateBuilder gateBuilder) {
- // traffic-profile
- String message = "";
- boolean valid = true;
- TrafficProfile profile = gate.getTrafficProfile();
- if (profile == null) {
- message += " traffic-profile is required;";
- valid = false;
- } else {
- final ServiceClassName scn;
- final String error = validateMethod(TrafficProfile.class, profile, "getServiceClassName");
- if (error == null) {
- scn = profile.getServiceClassName();
- if (scn == null) {
- message += " traffic-profile.service-class-name missing;";
- valid = false;
- }
- } else {
- message += " traffic-profile.service-class-name invalid: must be 2-16 characters " + error;
- scn = null;
- valid = false;
- }
- if (! valid) {
- final TrafficProfileBuilder profileBuilder = new TrafficProfileBuilder();
- // TODO FIXME - scn is always null???
- profileBuilder.setServiceClassName(scn);
- profile = profileBuilder.build();
- // update the gate
- gateBuilder.setTrafficProfile(profile);
- }
- }
- if (! valid) {
- logger.error(message);
- }
- return valid;
- }
-
- // TODO FIXME - Break this method apart
- private boolean validateClassifier(final Gate gate, final GateBuilder gateBuilder) {
- // validate classifier
- String message = "";
- boolean valid = true;
- int count = 0;
- Classifier classifier = gate.getClassifier();
- // SIP
- final Ipv4Address sip;
- String error = validateMethod(Classifier.class, classifier, "getSrcIp");
- if (error == null) {
- sip = classifier.getSrcIp();
- count++;
- } else {
- message += " classifier.srcIp invalid: - " + error;
- sip = null;
- valid = false;
- }
- // DIP
- final Ipv4Address dip;
- error = validateMethod(Classifier.class, classifier, "getDstIp");
- if (error == null) {
- dip = classifier.getDstIp();
- count++;
- } else {
- message += " classifier.dstIp invalid: - " + error;
- dip = null;
- valid = false;
- }
- // Protocol
- final TpProtocol proto;
- error = validateMethod(Classifier.class, classifier, "getProtocol");
- if (error == null) {
- proto = classifier.getProtocol();
- count++;
- } else {
- message += " classifier.protocol invalid: - " + error;
- proto = null;
- valid = false;
- }
- // Source Port
- final PortNumber sport;
- error = validateMethod(Classifier.class, classifier, "getSrcPort");
- if (error == null) {
- sport = classifier.getSrcPort();
- count++;
- } else {
- message += " classifier.srcPort invalid: - " + error;
- sport = null;
- valid = false;
- }
- // Destination Port
- final PortNumber dport;
- error = validateMethod(Classifier.class, classifier, "getDstPort");
- if (error == null) {
- dport = classifier.getDstPort();
- count++;
- } else {
- message += " classifier.dstPort invalid: - " + error;
- dport = null;
- valid = false;
- }
- // TOS
- final TosByte tosByte;
- error = validateMethod(Classifier.class, classifier, "getTosByte");
- if (error == null) {
- tosByte = classifier.getTosByte();
- count++;
- } else {
- message += " classifier.tosByte invalid: " + error;
- tosByte = null;
- valid = false;
- }
- final TosByte tosMask;
- error = validateMethod(Classifier.class, classifier, "getTosMask");
- if (error == null) {
- tosMask = classifier.getTosMask();
- if (tosByte != null && tosMask == null) {
- message += " classifier.tosMask missing;";
- valid = false;
- }
- } else {
- message += " classifier.tosMask invalid: " + error;
- tosMask = null;
- valid = false;
- }
- if (count == 0) {
- message += " classifer must have at least one match field";
- valid = false;
- }
- if (! valid) {
- final ClassifierBuilder cBuilder = new ClassifierBuilder();
- cBuilder.setSrcIp(sip);
- cBuilder.setDstIp(dip);
- cBuilder.setProtocol(proto);
- cBuilder.setSrcPort(sport);
- cBuilder.setDstPort(dport);
- cBuilder.setTosByte(tosByte);
- cBuilder.setTosMask(tosMask);
- classifier = cBuilder.build();
- gateBuilder.setClassifier(classifier);
- logger.error(message);
- }
- return valid;
- }
-
- // TODO FIXME - breakup this method
- private boolean validateExtClassifier(final Gate gate, final GateBuilder gateBuilder) {
- // validate ext-classifier
- String message = "";
- String error;
- boolean valid = true;
- int count = 0;
- ExtClassifier extClassifier = gate.getExtClassifier();
- // SIP & mask
- final Ipv4Address sip;
- error = validateMethod(ExtClassifier.class, extClassifier, "getSrcIp");
- if (error == null) {
- sip = extClassifier.getSrcIp();
- count++;
- } else {
- message += " ext-classifier.srcIp invalid: - " + error;
- sip = null;
- valid = false;
- }
- final Ipv4Address sipMask;
- error = validateMethod(ExtClassifier.class, extClassifier, "getSrcIpMask");
- if (error == null) {
- sipMask = extClassifier.getSrcIpMask();
- count++;
- } else {
- message += " ext-classifier.srcIpMask invalid: - " + error;
- sipMask = null;
- valid = false;
- }
- if (sip != null && sipMask == null) {
- message += " ext-classifier.srcIpMask missing";
- valid = false;
- }
- // DIP & mask
- final Ipv4Address dip;
- error = validateMethod(ExtClassifier.class, extClassifier, "getDstIp");
- if (error == null) {
- dip = extClassifier.getDstIp();
- count++;
- } else {
- message += " ext-classifier.dstIp invalid: - " + error;
- dip = null;
- valid = false;
- }
- final Ipv4Address dipMask;
- error = validateMethod(ExtClassifier.class, extClassifier, "getDstIpMask");
- if (error == null) {
- dipMask = extClassifier.getDstIpMask();
- count++;
- } else {
- message += " ext-classifier.srcIpMask invalid: - " + error;
- dipMask = null;
- valid = false;
- }
- if (dip != null && dipMask == null) {
- message += " ext-classifier.dstIpMask missing;";
- valid = false;
- }
- // Protocol
- final TpProtocol proto;
- error = validateMethod(ExtClassifier.class, extClassifier, "getProtocol");
- if (error == null) {
- proto = extClassifier.getProtocol();
- count++;
- } else {
- message += " ext-classifier.protocol invalid: - " + error;
- proto = null;
- valid = false;
- }
- // Source port range
- final PortNumber sportStart;
- error = validateMethod(ExtClassifier.class, extClassifier, "getSrcPortStart");
- if (error == null) {
- sportStart = extClassifier.getSrcPortStart();
- count++;
- } else {
- message += " ext-classifier.srcPortStart invalid: - " + error;
- sportStart = null;
- valid = false;
- }
- final PortNumber sportEnd;
- error = validateMethod(ExtClassifier.class, extClassifier, "getSrcPortEnd");
- if (error == null) {
- sportEnd = extClassifier.getSrcPortEnd();
- count++;
- } else {
- message += " ext-classifier.srcPortEnd invalid: - " + error;
- sportEnd = null;
- valid = false;
- }
- if (sportStart != null && sportEnd != null) {
- if (sportStart.getValue() > sportEnd.getValue()) {
- message += " ext-classifier.srcPortStart greater than srcPortEnd";
- valid = false;
- }
- }
- // Destination port range
- final PortNumber dportStart;
- error = validateMethod(ExtClassifier.class, extClassifier, "getDstPortStart");
- if (error == null) {
- dportStart = extClassifier.getDstPortStart();
- count++;
- } else {
- message += " ext-classifier.dstPortStart invalid: - " + error;
- dportStart = null;
- valid = false;
- }
- final PortNumber dportEnd;
- error = validateMethod(ExtClassifier.class, extClassifier, "getDstPortEnd");
- if (error == null) {
- dportEnd = extClassifier.getDstPortEnd();
- count++;
- } else {
- message += " ext-classifier.dstPortEnd invalid: - " + error;
- dportEnd = null;
- valid = false;
- }
- if (dportStart != null && dportEnd != null) {
- if (dportStart.getValue() > dportEnd.getValue()) {
- message += " ext-classifier.dstPortStart greater than dstPortEnd";
- valid = false;
- }
- }
- // TOS byte
- final TosByte tosByte;
- error = validateMethod(ExtClassifier.class, extClassifier, "getTosByte");
- if (error == null) {
- tosByte = extClassifier.getTosByte();
- count++;
- } else {
- message += " ext-classifier.tosByte invalid: " + error;
- tosByte = null;
- valid = false;
- }
- final TosByte tosMask;
- error = validateMethod(ExtClassifier.class, extClassifier, "getTosMask");
- if (error == null) {
- tosMask = extClassifier.getTosMask();
- if (tosByte != null && tosMask == null) {
- message += " ext-classifier.tosMask missing;";
- valid = false;
- }
- } else {
- message += " ext-classifier.tosMask invalid: " + error;
- tosMask = null;
- valid = false;
- }
- if (count == 0) {
- message += " ext-classifer must have at least one match field";
- valid = false;
- }
- if (! valid) {
- final ExtClassifierBuilder cBuilder = new ExtClassifierBuilder();
- cBuilder.setSrcIp(sip);
- cBuilder.setSrcIpMask(sipMask);
- cBuilder.setDstIp(dip);
- cBuilder.setDstIpMask(dipMask);
- cBuilder.setProtocol(proto);
- cBuilder.setSrcPortStart(sportStart);
- cBuilder.setSrcPortEnd(sportEnd);
- cBuilder.setDstPortStart(dportStart);
- cBuilder.setDstPortEnd(dportEnd);
- cBuilder.setTosByte(tosByte);
- cBuilder.setTosMask(tosMask);
- extClassifier = cBuilder.build();
- gateBuilder.setExtClassifier(extClassifier);
- logger.error(message);
- }
- return valid;
- }
-
- // TODO FIXME - break apart this method.
- private boolean validateIpv6Classifier(final Gate gate, final GateBuilder gateBuilder) {
- // validate ipv6-classifier
- String message = "";
- String error;
- boolean valid = true;
- int count = 0;
- Ipv6Classifier ipv6Classifier = gate.getIpv6Classifier();
- // Source IPv6 prefix
- final Ipv6Prefix sip6;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getSrcIp6");
- if (error == null) {
- sip6 = ipv6Classifier.getSrcIp6();
- count++;
- } else {
- message += " ipv6-classifier.srcIp invalid: - " + error;
- sip6 = null;
- valid = false;
- }
- // Destination IPv6 prefix
- final Ipv6Prefix dip6;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getDstIp6");
- if (error == null) {
- dip6 = ipv6Classifier.getDstIp6();
- count++;
- } else {
- message += " ipv6-classifier.dstIp invalid: - " + error;
- dip6 = null;
- valid = false;
- }
- // Flow label
- Long flowLabel;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getFlowLabel");
- if (error == null) {
- flowLabel = ipv6Classifier.getFlowLabel();
- if (flowLabel > 1048575) {
- message += " ipv6-classifier.flowLabel invalid: - must be 0..1048575";
- flowLabel = null;
- valid = false;
- } else {
- count++;
- }
- } else {
- message += " ipv6-classifier.flowLabel invalid: - " + error;
- flowLabel = null;
- valid = false;
- }
- // Next Hdr
- final TpProtocol nxtHdr;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getNextHdr");
- if (error == null) {
- nxtHdr = ipv6Classifier.getNextHdr();
- count++;
- } else {
- message += " ipv6-classifier.nextHdr invalid: - " + error;
- nxtHdr = null;
- valid = false;
- }
- // Source port range
- final PortNumber sportStart;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getSrcPortStart");
- if (error == null) {
- sportStart = ipv6Classifier.getSrcPortStart();
- count++;
- } else {
- message += " ipv6-classifier.srcPortStart invalid: - " + error;
- sportStart = null;
- valid = false;
- }
- final PortNumber sportEnd;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getSrcPortEnd");
- if (error == null) {
- sportEnd = ipv6Classifier.getSrcPortEnd();
- count++;
- } else {
- message += " ipv6-classifier.srcPortEnd invalid: - " + error;
- sportEnd = null;
- valid = false;
- }
- if (sportStart != null && sportEnd != null) {
- if (sportStart.getValue() > sportEnd.getValue()) {
- message += " ipv6-classifier.srcPortStart greater than srcPortEnd";
- valid = false;
- }
- }
- // Destination port range
- final PortNumber dportStart;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getDstPortStart");
- if (error == null) {
- dportStart = ipv6Classifier.getDstPortStart();
- count++;
- } else {
- message += " ipv6-classifier.dstPortStart invalid: - " + error;
- dportStart = null;
- valid = false;
- }
- final PortNumber dportEnd;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getDstPortEnd");
- if (error == null) {
- dportEnd = ipv6Classifier.getDstPortEnd();
- count++;
- } else {
- message += " ipv6-classifier.dstPortEnd invalid: - " + error;
- dportEnd = null;
- valid = false;
- }
- if (dportStart != null && dportEnd != null) {
- if (dportStart.getValue() > dportEnd.getValue()) {
- message += " ipv6-classifier.dstPortStart greater than dstPortEnd";
- valid = false;
- }
- }
- // TC byte
- final TosByte tcLow;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getTcLow");
- if (error == null) {
- tcLow = ipv6Classifier.getTcLow();
- count++;
- } else {
- message += " ipv6-classifier.tc-low invalid: " + error;
- tcLow = null;
- valid = false;
- }
- final TosByte tcHigh;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getTcHigh");
- if (error == null) {
- tcHigh = ipv6Classifier.getTcHigh();
- count++;
- } else {
- message += " ipv6-classifier.tc-high invalid: " + error;
- tcHigh = null;
- valid = false;
- }
- if (tcLow != null && tcHigh != null) {
- if (tcLow.getValue() > tcHigh.getValue()) {
- message += " ipv6-classifier.tc-low is greater than tc-high";
- valid = false;
- }
- }
- final TosByte tcMask;
- error = validateMethod(Ipv6Classifier.class, ipv6Classifier, "getTcMask");
- if (error == null) {
- tcMask = ipv6Classifier.getTcMask();
- } else {
- message += " ipv6-classifier.tc-mask invalid: " + error;
- tcMask = null;
- valid = false;
- }
- if (tcLow != null && tcHigh != null && tcMask == null) {
- message += " ipv6-classifier.tc-mask missing;";
- valid = false;
- }
- if (count == 0) {
- message += " ipv6-classifer must have at least one match field";
- valid = false;
- }
- // rebuild ?
- if (! valid) {
- final Ipv6ClassifierBuilder cBuilder = new Ipv6ClassifierBuilder();
- cBuilder.setSrcIp6(sip6);
- cBuilder.setDstIp6(dip6);
- cBuilder.setFlowLabel(flowLabel);
- cBuilder.setNextHdr(nxtHdr);
- cBuilder.setSrcPortStart(sportStart);
- cBuilder.setSrcPortEnd(sportEnd);
- cBuilder.setDstPortStart(dportStart);
- cBuilder.setDstPortEnd(dportEnd);
- cBuilder.setTcLow(tcLow);
- cBuilder.setTcHigh(tcHigh);
- cBuilder.setTcMask(tcMask);
- ipv6Classifier = cBuilder.build();
- gateBuilder.setIpv6Classifier(ipv6Classifier);
- logger.error(message);
- }
- return valid;
- }
-
- // TODO FIXME - Do we really want the gate parameter object to be muted by this method?
- private boolean validateGate(Gate gate) {
- // validate gate elements and null out invalid elements as we go
- final GateBuilder gateBuilder = new GateBuilder();
- String message = "";
- boolean rebuild = false;
- // gate-spec
- if (! validateGateSpec(gate, gateBuilder)) {
- rebuild = true;
- }
- // traffic-profile
- if (! validateTrafficProfile(gate, gateBuilder)) {
- rebuild = true;
- }
- // classifiers (one of legacy classifier, ext-classifier, or ipv6 classifier
- final Classifier classifier = gate.getClassifier();
- final ExtClassifier extClassifier = gate.getExtClassifier();
- final Ipv6Classifier ipv6Classifier = gate.getIpv6Classifier();
- int count = 0;
- if (classifier != null) { count++; }
- if (extClassifier != null) { count++; }
- if (ipv6Classifier != null) { count++; }
- if (count < 1){
- message = " Missing classifer: must have only 1 of classifier, ext-classifier, or ipv6-classifier";
- rebuild = true;
- } else if (count > 1) {
- message = "Multiple classifiers: must have only 1 of classifier, ext-classifier, or ipv6-classifier";
- rebuild = true;
- } else if (count == 1) {
- if (classifier != null) {
- // validate classifier
- if (! validateClassifier(gate, gateBuilder)) {
- rebuild = true;
- }
- } else if (extClassifier != null) {
- //validate ext-classifier
- if (! validateExtClassifier(gate, gateBuilder)) {
- rebuild = true;
- }
- } else if (ipv6Classifier != null) {
- // TODO FIXME - ipv6Classifier is always null???
- // validate ipv6-classifier
- if (! validateIpv6Classifier(gate, gateBuilder)) {
- rebuild = true;
- }
- }
- }
- // rebuild the gate object with valid data and set the response
- if (rebuild) {
- gateBuilder.setGateId(gate.getGateId());
- gateBuilder.setKey(gate.getKey());
- // TODO FIXME - the input parameter "gate" is being muted here???
- gate = gateBuilder.build();
- logger.error("Gate: {} - {}", gate, message);
- }
- return (! rebuild);
- }
-
- private boolean validateAmId(final Ccap ccap, final CcapBuilder ccapBuilder) {
- // amId
- String message = "";
- String error;
- boolean valid = true;
- AmId amId = ccap.getAmId();
- if (amId == null) {
- message += " amId is required;";
- valid = false;
- } else {
- final Integer amTag;
- error = validateMethod(AmId.class, amId, "getAmTag");
- if (error == null) {
- amTag = amId.getAmTag();
- if (amTag == null) {
- message += " amId.amTag missing;";
- valid = false;
- }
- } else {
- message += " amId.amTag invalid: " + error;
- amTag = null;
- valid = false;
- }
- final Integer amType;
- error = validateMethod(AmId.class, amId, "getAmType");
- if (error == null) {
- amType = amId.getAmType();
- if (amType == null) {
- message += " amId.amType missing;";
- valid = false;
- }
- } else {
- message += " amId.amType invalid: " + error;
- amType = null;
- valid = false;
- }
- if (! valid) {
- final AmIdBuilder amIdBuilder = new AmIdBuilder();
- amIdBuilder.setAmTag(amTag);
- amIdBuilder.setAmType(amType);
- amId = amIdBuilder.build();
- ccapBuilder.setAmId(amId);
- }
- }
- if (! valid) {
- logger.error(message);
- }
- return valid;
- }
-
- private boolean validateConnection(final Ccap ccap, final CcapBuilder ccapBuilder) {
- // connection
- String message = "";
- String error;
- boolean valid = true;
- Connection conn = ccap.getConnection();
- if (conn == null) {
- message += " connection is required;";
- valid = false;
- } else {
- // IP address
- final IpAddress ipAddress;
- error = validateMethod(Connection.class, conn, "getIpAddress");
- if (error == null) {
- ipAddress = conn.getIpAddress();
- if (ipAddress == null) {
- message += " connection.ipAddress missing;";
- valid = false;
- }
- } else {
- message += " connection.ipAddress invalid: " + error;
- ipAddress = null;
- valid = false;
- }
- // Port number
- final PortNumber portNum;
- error = validateMethod(Connection.class, conn, "getPort");
- if (error == null) {
- portNum = conn.getPort();
- } else {
- message += " connection.port invalid: " + error;
- portNum = null;
- valid = false;
- }
- if (! valid) {
- final ConnectionBuilder connBuilder = new ConnectionBuilder();
- connBuilder.setIpAddress(ipAddress);
- connBuilder.setPort(portNum);
- conn = connBuilder.build();
- ccapBuilder.setConnection(conn);
- }
- }
- if (! valid) {
- logger.error(message);
- }
- return valid;
- }
-
- private boolean validateSubscriberSubnets(final Ccap ccap, final CcapBuilder ccapBuilder) {
- // subscriber-subnets
- String message = "";
- String error;
- boolean valid = true;
- List<IpPrefix> subnets = null;
- error = validateMethod(Ccap.class, ccap, "getSubscriberSubnets");
- if (error == null) {
- subnets = ccap.getSubscriberSubnets();
- if (subnets == null) {
- message += " subscriber-subnets is required;";
- valid = false;
- }
- } else {
- message += " subscriber-subnets contains invalid IpPrefix - must be <ipaddress>/<prefixlen> format;" + error;
- valid = false;
- }
- if (! valid) {
- // TODO FIXME - subnets is always null???
- ccapBuilder.setSubscriberSubnets(subnets);
- logger.error(message);
- }
- return valid;
- }
-
- private boolean validateUpstreamScns(final Ccap ccap, final CcapBuilder ccapBuilder) {
- // upstream-scns
- String message = "";
- String error;
- boolean valid = true;
- List<ServiceClassName> usScns = null;
- error = validateMethod(Ccap.class, ccap, "getUpstreamScns");
- if (error == null) {
- usScns = ccap.getUpstreamScns();
- if (usScns == null) {
- message += " upstream-scns is required;";
- valid = false;
- }
- } else {
- message += " upstream-scns contains invalid SCN - must be 2-16 characters;" + error;
- valid = false;
- }
- if (! valid) {
- // TODO FIXME - usScns is always null???
- ccapBuilder.setUpstreamScns(usScns);
- logger.error(message);
- }
- return valid;
- }
-
- private boolean validateDownstreamScns(final Ccap ccap, final CcapBuilder ccapBuilder) {
- // downstream-scns
- String message = "";
- boolean valid = true;
- List<ServiceClassName> dsScns = null;
- final String error = validateMethod(Ccap.class, ccap, "getDownstreamScns");
- if (error == null) {
- dsScns = ccap.getDownstreamScns();
- if (dsScns == null) {
- message += " downstream-scns is required;";
- valid = false;
- }
- } else {
- message += " downstream-scns contains invalid SCN - must be 2-16 characters;" + error;
- valid = false;
- }
- if (! valid) {
- // TODO FIXME - dsScns is always null???
- ccapBuilder.setDownstreamScns(dsScns);
- logger.error(message);
- }
- return valid;
- }
-
-
- // TODO FIXME - Do we really want the ccap parameter object to be muted by this method?
- private boolean validateCcap(Ccap ccap) {
- // validate ccap and null out invalid elements as we go
- final CcapBuilder ccapBuilder = new CcapBuilder();
- String message = "";
- boolean rebuild = false;
- // amId
- if ( ! validateAmId(ccap, ccapBuilder)) {
- rebuild = true;
- }
- // connection
- if ( ! validateConnection(ccap, ccapBuilder)) {
- rebuild = true;
- }
- // subscriber-subnets
- if ( ! validateSubscriberSubnets(ccap, ccapBuilder)) {
- rebuild = true;
- }
- // upstream-scns
- if ( ! validateUpstreamScns(ccap, ccapBuilder)) {
- rebuild = true;
- }
- // downstream-scns
- if ( ! validateDownstreamScns(ccap, ccapBuilder)) {
- rebuild = true;
- }
- // rebuild the ccap object with valid data and set the response
- if (rebuild) {
- ccapBuilder.setCcapId(ccap.getCcapId());
- ccapBuilder.setKey(ccap.getKey());
- // TODO FIXME - the input parameter "ccap" is being muted here???
- ccap = ccapBuilder.build();
- logger.error("Ccap: {} - {} ", ccap, message);
- }
- return (! rebuild);
- }
-}
-
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+import org.opendaylight.yangtools.yang.binding.InstanceIdentifier;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Validates restconf data that is supplied by users
+ *
+ * @author rvail
+ */
+public class DataValidator {
+ private static final Logger logger = LoggerFactory.getLogger(DataValidator.class);
+
+ private final ValidatorProvider validatorProvider;
+
+ public DataValidator(@Nonnull final ValidatorProvider validatorProvider) {
+ this.validatorProvider = checkNotNull(validatorProvider);
+ }
+
+ public Map<InstanceIdentifier<?>, ValidationException> validate(
+ @Nonnull final Map<InstanceIdentifier<?>, DataObject> dataObjectMap,
+ @Nonnull final Validator.Extent extent) {
+ checkNotNull(dataObjectMap);
+
+ Map<InstanceIdentifier<?>, ValidationException> exceptionMap = Maps.newHashMap();
+
+ for (Map.Entry<InstanceIdentifier<?>, DataObject> entry : dataObjectMap.entrySet()) {
+ final InstanceIdentifier<?> iid = entry.getKey();
+ final DataObject data = entry.getValue();
+
+ try {
+ validate(iid, data, extent);
+ } catch (ValidationException e) {
+ exceptionMap.put(iid, e);
+ logger.debug("invalid data: {}", data, e);
+ } catch (NoSuchElementException e) {
+ logger.error("Unable to find validator for data: {}", data, e);
+ }
+ }
+
+ return exceptionMap;
+ }
+
+ public void validate(@Nonnull InstanceIdentifier<?> iid, @Nonnull final DataObject dataObject,
+ @Nonnull final Validator.Extent extent) throws ValidationException {
+ checkNotNull(iid);
+ checkNotNull(dataObject);
+ validatorProvider.validate(iid.getTargetType(), dataObject, extent);
+ }
+
+ public <T extends DataObject> Map<InstanceIdentifier<T>, ValidationException> validateOneType(
+ @Nonnull final Map<InstanceIdentifier<T>, T> dataObjectMap, @Nonnull final Validator.Extent extent) {
+ checkNotNull(dataObjectMap);
+
+ Map<InstanceIdentifier<T>, ValidationException> exceptionMap = Maps.newHashMap();
+
+ for (Map.Entry<InstanceIdentifier<T>, T> entry : dataObjectMap.entrySet()) {
+ final InstanceIdentifier<T> iid = entry.getKey();
+ final T data = entry.getValue();
+
+ try {
+ validate(iid, data, extent);
+ } catch (ValidationException e) {
+ exceptionMap.put(iid, e);
+ logger.debug("invalid data: {}", data, e);
+ } catch (NoSuchElementException e) {
+ logger.error("Unable to find validator for data: {}", data, e);
+ }
+ }
+
+ return exceptionMap;
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.ImmutableList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Iterator;
+
+/**
+ * @author rvail
+ */
+public class ValidationException extends Exception {
+
+ private final ImmutableList<String> errorMessages;
+
+ public ValidationException(final String... errorMessages) {
+ super(concat(Arrays.asList(errorMessages)));
+ this.errorMessages = ImmutableList.copyOf(errorMessages);
+ }
+
+ private static String concat(Collection<String> strings) {
+ checkNotNull(strings);
+
+ final Iterator<String> iter = strings.iterator();
+ if (!iter.hasNext()) {
+ return "";
+ }
+
+ StringBuilder sb = new StringBuilder(iter.next());
+ while (iter.hasNext()) {
+ sb.append(" : ").append(iter.next());
+ }
+
+ return sb.toString();
+ }
+
+ public ValidationException(final Collection<String> errorMessages) {
+ super(concat(errorMessages));
+ this.errorMessages = ImmutableList.copyOf(errorMessages);
+ }
+
+ public ImmutableList<String> getErrorMessages() {
+ return errorMessages;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation;
+
+/**
+ * @author rvail
+ */
+public interface Validator<T> {
+
+ void validate(final T data, Extent extent) throws ValidationException;
+
+ enum Extent {
+ NODE_ONLY,
+ NODE_AND_SUBTREE
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation;
+
+import java.util.NoSuchElementException;
+import javax.annotation.Nonnull;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+/**
+ * Helper class to hide the casting needed to store all the validators in one collection.
+ * Types remain consistent by using generics on the put method.
+ *
+ * @author rvail
+ */
+public interface ValidatorProvider {
+
+ /**
+ * Add a new validator or replace an old validator to this provider.
+ *
+ * @param tClass
+ * The Class of T
+ * @param validator
+ * The validator for the Class T
+ * @param <T>
+ * The type being validated
+ */
+ <T extends DataObject> void put(@Nonnull final Class<T> tClass, @Nonnull final Validator<T> validator);
+
+ /**
+ * Gets the validator for a particular type
+ *
+ * @param tClass
+ * The Class of T
+ * @param <T>
+ * The type to be validated
+ * @return a Validator instance
+ * @throws NoSuchElementException
+ * if a Validator for the passed in type does not exist on this provider.
+ */
+ <T extends DataObject> Validator<T> validatorFor(@Nonnull final Class<T> tClass);
+
+ /**
+ * Helper method to get a validator and then call validate with the supplied DataObject.
+ *
+ * @param tClass
+ * The class type to validate
+ * @param data
+ * The DataObject instance to validate
+ * @param extent
+ * The extend to validate with
+ * @param <T>
+ * The type of tClass
+ * @throws ValidationException
+ * if validation fails
+ * @throws IllegalArgumentException
+ * if data is not assignable from tClass
+ */
+ <T extends DataObject> void validate(@Nonnull final Class<T> tClass, @Nonnull final DataObject data,
+ @Nonnull final Validator.Extent extent) throws ValidationException;
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation;
+
+/**
+ * @author rvail
+ */
+public interface ValidatorProviderFactory {
+
+ ValidatorProvider build();
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidatorProvider;
+import org.opendaylight.controller.packetcable.provider.validation.ValidatorProviderFactory;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.ccaps.AmIdValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.ccaps.CcapValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.ccaps.CcapsValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.ccaps.ConnectionValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.Ccaps;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.AmId;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.Connection;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.Ccap;
+
+/**
+ * A ValidatorProviderFactory that can provide validators for types under packetcable:ccaps.
+ *
+ * @author rvail
+ */
+public class CcapsValidatorProviderFactory implements ValidatorProviderFactory {
+
+ @Override
+ public ValidatorProvider build() {
+ return addCcapsValidators(new ValidatorProviderImpl());
+ }
+
+ public static ValidatorProvider addCcapsValidators(ValidatorProvider provider) {
+ provider.put(Ccaps.class, new CcapsValidator());
+ provider.put(Ccap.class, new CcapValidator());
+ provider.put(AmId.class, new AmIdValidator());
+ provider.put(Connection.class, new ConnectionValidator());
+
+ return provider;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidatorProvider;
+import org.opendaylight.controller.packetcable.provider.validation.ValidatorProviderFactory;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.AppValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.AppsValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.GateValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.GatesValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.SubscriberValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.SubscribersValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.TrafficProfileValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier.ClassifierValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier.ExtClassifierValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier.Ipv6ClassifierValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.classifier.Classifier;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.ext.classifier.ExtClassifier;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.Apps;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.App;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.Subscribers;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.Subscriber;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.Gates;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.ipv6.classifier.Ipv6Classifier;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.traffic.profile.TrafficProfile;
+
+/**
+ * * A ValidatorProviderFactory that can provide validators for types under packetcable:qos.
+ *
+ * @author rvail
+ */
+public class QosValidatorProviderFactory implements ValidatorProviderFactory {
+
+ @Override
+ public ValidatorProvider build() {
+ return addQosValidators(new ValidatorProviderImpl());
+ }
+
+ public static ValidatorProvider addQosValidators(ValidatorProvider provider) {
+ provider.put(Apps.class, new AppsValidator());
+ provider.put(App.class, new AppValidator());
+
+ provider.put(Subscribers.class, new SubscribersValidator());
+ provider.put(Subscriber.class, new SubscriberValidator());
+
+ provider.put(Gates.class, new GatesValidator());
+ provider.put(Gate.class, new GateValidator());
+
+ provider.put(TrafficProfile.class, new TrafficProfileValidator());
+
+ provider.put(Classifier.class, new ClassifierValidator());
+ provider.put(ExtClassifier.class, new ExtClassifierValidator());
+ provider.put(Ipv6Classifier.class, new Ipv6ClassifierValidator());
+
+ return provider;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidatorProvider;
+import org.opendaylight.controller.packetcable.provider.validation.ValidatorProviderFactory;
+
+/**
+ * A ValidatorProviderFactory that returns providers that can handle all known types.
+ *
+ * @author rvail
+ */
+public class ValidatorProviderFactoryImpl implements ValidatorProviderFactory {
+
+ @Override
+ public ValidatorProvider build() {
+ ValidatorProvider provider = new ValidatorProviderImpl();
+
+ CcapsValidatorProviderFactory.addCcapsValidators(provider);
+ QosValidatorProviderFactory.addQosValidators(provider);
+
+ return provider;
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl;
+
+import static com.google.common.base.Preconditions.checkNotNull;
+
+import com.google.common.collect.Maps;
+import java.util.Map;
+import java.util.NoSuchElementException;
+import javax.annotation.Nonnull;
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.Validator;
+import org.opendaylight.controller.packetcable.provider.validation.ValidatorProvider;
+import org.opendaylight.yangtools.yang.binding.DataObject;
+
+
+/**
+ * {@inheritDoc}
+ *
+ * @author rvail
+ */
+public class ValidatorProviderImpl implements ValidatorProvider {
+
+ private final Map<Class<? extends DataObject>, Validator<? extends DataObject>> validatorMap;
+
+ public ValidatorProviderImpl() {
+ this.validatorMap = Maps.newHashMap();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public <T extends DataObject> void put(@Nonnull final Class<T> tClass, @Nonnull final Validator<T> validator) {
+ checkNotNull(tClass);
+ checkNotNull(validator);
+ validatorMap.put(tClass, validator);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public <T extends DataObject> void validate(@Nonnull final Class<T> tClass, @Nonnull final DataObject data,
+ @Nonnull final Validator.Extent extent) throws ValidationException {
+ if (!tClass.isAssignableFrom(data.getClass())) {
+ throw new IllegalArgumentException(
+ String.format("data must be the same type as tClass, got=%s : expected=%s",
+ data.getImplementedInterface(), tClass));
+ }
+ // We are checking the type of data above
+ @SuppressWarnings("unchecked") T tData = (T) data;
+ validatorFor(tClass).validate(tData, extent);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public <T extends DataObject> Validator<T> validatorFor(@Nonnull final Class<T> tClass) {
+ checkNotNull(tClass);
+ if (validatorMap.containsKey(tClass)) {
+ // validation is done via the put method all key/value pairs are for the same type T
+ @SuppressWarnings("unchecked") Validator<T> result = (Validator<T>) validatorMap.get(tClass);
+ return result;
+ }
+ throw new NoSuchElementException("Entry not found for key: " + tClass);
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import javax.annotation.concurrent.NotThreadSafe;
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.Validator;
+
+/**
+ * Helper class to help support lazy initialization of error message array.<br>
+ * This delays array creation until bad data is found.
+ * <br><br>
+ * <strong>Subclasses must call {@link #throwErrorsIfNeeded()} at the end of validate()</strong>
+ *
+ * @author rvail
+ */
+@NotThreadSafe
+public abstract class AbstractValidator<T> implements Validator<T> {
+
+ private ArrayList<String> errorMessages = null;
+
+ /**
+ * If any error messages have been added to the list returned by {@link #getErrorMessages()}
+ * then a ValidationException will be thrown with those error messages.
+ *
+ * @throws ValidationException
+ */
+ protected void throwErrorsIfNeeded() throws ValidationException {
+ if (errorMessages != null && !errorMessages.isEmpty()) {
+ ValidationException exception = new ValidationException(errorMessages);
+ resetErrorMessages();
+ throw exception;
+ }
+ }
+
+ /**
+ * sets the error message list to null.
+ */
+ protected void resetErrorMessages() {
+ errorMessages = null;
+ }
+
+ /**
+ * Checks if the passed in object is null. If it is, then an error message will be
+ * appended to the current list of errors.
+ *
+ * @param obj
+ * The object that must not be null.
+ * @param name
+ * The name of the object (will be used in the error message).
+ */
+ protected void mustExist(Object obj, String name) {
+ if (obj == null) {
+ getErrorMessages().add(name + " must exist");
+ }
+ }
+
+ /**
+ * Lazy initalizer of an array list of error messages.
+ *
+ * @return The array list of error messages
+ */
+ protected ArrayList<String> getErrorMessages() {
+ if (errorMessages == null) {
+ errorMessages = new ArrayList<>(2);
+ }
+ return errorMessages;
+ }
+
+ /**
+ * Checks if the passed in collection is null or empty. If it is then an error
+ * will be appended to the current list of errors.
+ *
+ * @param collection
+ * The collection to test
+ * @param name
+ * The name of the object (will be used in the error message)
+ */
+ protected void mustExistAndNotBeEmpty(Collection<?> collection, String name) {
+ if (collection == null) {
+ getErrorMessages().add(name + " must exist");
+ } else if (collection.isEmpty()) {
+ getErrorMessages().add(name + " must not be empty");
+ }
+ }
+
+ protected <C> void validateChild(Validator<C> validator, C child) {
+ try {
+ validator.validate(child, Extent.NODE_AND_SUBTREE);
+ } catch (ValidationException e) {
+ getErrorMessages().addAll(e.getErrorMessages());
+ }
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.ccaps;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.AmId;
+
+/**
+ * @author rvail
+ */
+public class AmIdValidator extends AbstractValidator<AmId> {
+
+ private static final String AM_TYPE = "amId.am-type";
+ private static final String AM_TAG = "amId.am-tag";
+
+ @Override
+ public void validate(final AmId amId, Extent extent) throws ValidationException {
+
+ if (amId == null) {
+ throw new ValidationException("amId must exist");
+ }
+
+ mustExist(amId.getAmTag(), AM_TAG);
+ mustExist(amId.getAmType(), AM_TYPE);
+
+ throwErrorsIfNeeded();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.ccaps;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.Ccap;
+
+/**
+ * @author rvail
+ */
+public class CcapValidator extends AbstractValidator<Ccap> {
+
+ private static final String CCAPID = "ccap.ccapid";
+ private static final String CONNECTION = "ccap.connection";
+ private static final String AM_ID = "ccap.amId";
+ private static final String UP_STREAM_SCNS = "ccap.upstream-scns";
+ private static final String DOWN_STREAM_SCNS = "ccap.downstream-scns";
+
+ private final AmIdValidator amIdValidator = new AmIdValidator();
+ private final ConnectionValidator connectionValidator = new ConnectionValidator();
+
+ @Override
+ public void validate(final Ccap ccap, Extent extent) throws ValidationException {
+ if (ccap == null) {
+ throw new ValidationException("ccap must exist");
+ }
+
+ mustExist(ccap.getCcapId(), CCAPID);
+
+ mustExistAndNotBeEmpty(ccap.getUpstreamScns(), UP_STREAM_SCNS);
+ mustExistAndNotBeEmpty(ccap.getDownstreamScns(), DOWN_STREAM_SCNS);
+
+ if (extent == Extent.NODE_AND_SUBTREE) {
+ validateChild(amIdValidator, ccap.getAmId());
+ validateChild(connectionValidator, ccap.getConnection());
+ } else {
+ mustExist(ccap.getAmId(), AM_ID);
+ mustExist(ccap.getConnection(), CONNECTION);
+ }
+
+ throwErrorsIfNeeded();
+ }
+
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.ccaps;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.Ccaps;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.Ccap;
+
+/**
+ * @author rvail
+ */
+public class CcapsValidator extends AbstractValidator<Ccaps> {
+
+ private final CcapValidator ccapValidator = new CcapValidator();
+
+ @Override
+ public void validate(final Ccaps ccaps, Extent extent) throws ValidationException {
+ if (ccaps == null) {
+ throw new ValidationException("ccaps must exist");
+ }
+
+ if (extent == Extent.NODE_AND_SUBTREE) {
+ for (Ccap ccap : ccaps.getCcap()) {
+ validateChild(ccapValidator, ccap);
+ }
+ }
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.ccaps;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccap.attributes.Connection;
+
+/**
+ * @author rvail
+ */
+public class ConnectionValidator extends AbstractValidator<Connection> {
+
+ private static final String IP_ADDRESS = "connection.ipAddress";
+ private static final String PORT = "connection.port";
+
+ @Override
+ public void validate(final Connection connection, Extent extent) throws ValidationException {
+ if (connection == null) {
+ throw new ValidationException("connection must exist");
+ }
+
+ mustExist(connection.getIpAddress(), IP_ADDRESS);
+
+ // Note PortNumber validates range on creation so only existence needs to be checked
+ mustExist(connection.getPort(), PORT);
+
+
+ throwErrorsIfNeeded();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.App;
+
+/**
+ * @author rvail
+ */
+public class AppValidator extends AbstractValidator<App> {
+
+ private static final String APP_ID = "app.appId";
+ private static final String SUBSCRIBERS = "app.subscribers";
+
+ private final SubscribersValidator subscribersValidator = new SubscribersValidator();
+
+ @Override
+ public void validate(final App app, final Extent extent) throws ValidationException {
+ if (app == null) {
+ throw new ValidationException("app must exist");
+ }
+
+ mustExist(app.getAppId(), APP_ID);
+ mustExist(app.getSubscribers(), SUBSCRIBERS);
+
+ if (extent == Extent.NODE_AND_SUBTREE) {
+ validateChild(subscribersValidator, app.getSubscribers());
+ }
+
+ throwErrorsIfNeeded();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.Apps;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.App;
+
+/**
+ * @author rvail
+ */
+public class AppsValidator extends AbstractValidator<Apps> {
+
+ private final AppValidator appValidator = new AppValidator();
+
+ @Override
+ public void validate(final Apps apps, final Extent extent) throws ValidationException {
+ if (apps == null) {
+ throw new ValidationException("apps must exist");
+ }
+ if (extent == Extent.NODE_AND_SUBTREE) {
+ for (App app : apps.getApp()) {
+ validateChild(appValidator, app);
+ }
+ }
+
+ throwErrorsIfNeeded();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gate.spec.GateSpec;
+
+/**
+ * @author rvail
+ */
+public class GateSpecValidatator extends AbstractValidator<GateSpec> {
+
+ private static final String DIRECTION = "gate-spec.direction";
+
+ @Override
+ public void validate(final GateSpec gateSpec, final Extent extent) throws ValidationException {
+ if (gateSpec == null) {
+ throw new ValidationException("gate-spec must exist");
+ }
+
+ // everything is optional
+
+// mustExist(gateSpec.getDirection(), DIRECTION);
+//
+// // dscp-tos-overwrite & dscp-tos-mask are optional
+ throwErrorsIfNeeded();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier.ClassifierValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier.ExtClassifierValidator;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier.Ipv6ClassifierValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
+
+/**
+ * @author rvail
+ */
+public class GateValidator extends AbstractValidator<Gate> {
+
+ private static final String GATE_ID = "gate.gateId";
+ private static final String GATE_SPEC = "gate.gate-spec";
+ private static final String TRAFFIC_PROFILE = "gate.traffic-profile";
+
+// private final GateSpecValidatator gateSpecValidatator = new GateSpecValidatator();
+ private final TrafficProfileValidator trafficProfileValidator = new TrafficProfileValidator();
+ private final ClassifierValidator classifierValidator = new ClassifierValidator();
+ private final ExtClassifierValidator extClassifierValidator = new ExtClassifierValidator();
+ private final Ipv6ClassifierValidator ipv6ClassifierValidator = new Ipv6ClassifierValidator();
+
+ @Override
+ public void validate(final Gate gate, final Extent extent) throws ValidationException {
+ if (gate == null) {
+ throw new ValidationException("gate must exist");
+ }
+
+ mustExist(gate.getGateId(), GATE_ID);
+
+ // all leafs in GateSpec are optional
+ // mustExist(gate.getGateSpec(), GATE_SPEC);
+
+ mustExist(gate.getTrafficProfile(), TRAFFIC_PROFILE);
+ if (extent == Extent.NODE_AND_SUBTREE) {
+// validateChild(gateSpecValidatator, gate.getGateSpec());
+ validateChild(trafficProfileValidator, gate.getTrafficProfile());
+ }
+
+ // Classifiers
+
+ if (gate.getClassifier() != null) {
+
+ // classifer is not null, ext and ipv6 must be null
+
+ if (gate.getExtClassifier() != null || gate.getIpv6Classifier() != null) {
+ getErrorMessages().add("Only one type of classifier is allowed");
+ }
+ else if (extent == Extent.NODE_AND_SUBTREE) {
+ validateChild(classifierValidator, gate.getClassifier());
+ }
+
+ }
+ else if (gate.getExtClassifier() != null) {
+
+ // classifer is null; ext is not null and ipv6 must be null
+
+ if (gate.getIpv6Classifier() != null) {
+ getErrorMessages().add("Only one type of classifier is allowed");
+ }
+ else if (extent == Extent.NODE_AND_SUBTREE) {
+ validateChild(extClassifierValidator, gate.getExtClassifier());
+ }
+
+ }
+ else if (gate.getIpv6Classifier() != null) {
+
+ // classifer and ext are null; ipv6 is not
+ if (extent == Extent.NODE_AND_SUBTREE) {
+ validateChild(ipv6ClassifierValidator, gate.getIpv6Classifier());
+ }
+
+ }
+ else {
+ getErrorMessages().add("a classifer is required");
+ }
+
+ throwErrorsIfNeeded();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.Gates;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
+
+/**
+ * @author rvail
+ */
+public class GatesValidator extends AbstractValidator<Gates> {
+
+ private final GateValidator gateValidator = new GateValidator();
+
+ @Override
+ public void validate(final Gates gates, final Extent extent) throws ValidationException {
+ if (gates == null) {
+ throw new ValidationException("gates must exist");
+ }
+
+ if (extent == Extent.NODE_AND_SUBTREE) {
+ for (Gate gate : gates.getGate()) {
+ validateChild(gateValidator, gate);
+ }
+ }
+
+ throwErrorsIfNeeded();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.Subscriber;
+
+/**
+ * @author rvail
+ */
+public class SubscriberValidator extends AbstractValidator<Subscriber> {
+
+ private static final String SUBSCRIBER_ID = "subscriber.subscriberId";
+ private static final String GATES = "subscriber.gates";
+
+ private final GatesValidator gatesValidator = new GatesValidator();
+
+ @Override
+ public void validate(final Subscriber subscriber, final Extent extent) throws ValidationException {
+ if (subscriber == null) {
+ throw new ValidationException("subscriber must exist");
+ }
+
+ mustExist(subscriber.getSubscriberId(), SUBSCRIBER_ID);
+ mustExist(subscriber.getGates(), GATES);
+
+ if (extent == Extent.NODE_AND_SUBTREE) {
+ validateChild(gatesValidator, subscriber.getGates());
+ }
+
+ throwErrorsIfNeeded();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.Subscribers;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.gates.apps.app.subscribers.Subscriber;
+
+/**
+ * @author rvail
+ */
+public class SubscribersValidator extends AbstractValidator<Subscribers> {
+
+ private final SubscriberValidator subscriberValidator = new SubscriberValidator();
+
+ @Override
+ public void validate(final Subscribers subscribers, final Extent extent) throws ValidationException {
+ if (subscribers == null) {
+ throw new ValidationException("subscribers must exist");
+ }
+
+ if (extent == Extent.NODE_AND_SUBTREE) {
+ for (Subscriber subscriber : subscribers.getSubscriber()) {
+ validateChild(subscriberValidator , subscriber);
+ }
+ }
+
+ throwErrorsIfNeeded();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.traffic.profile.TrafficProfile;
+
+/**
+ * @author rvail
+ */
+public class TrafficProfileValidator extends AbstractValidator<TrafficProfile> {
+
+ private static final String SCN = "service-class-name";
+
+ @Override
+ public void validate(final TrafficProfile trafficProfile, final Extent extent) throws ValidationException {
+ if (trafficProfile == null) {
+ throw new ValidationException("traffic-profile must exist");
+ }
+
+ mustExist(trafficProfile.getServiceClassName(), SCN);
+
+ throwErrorsIfNeeded();
+ }
+
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.classifier.Classifier;
+
+/**
+ * @author rvail
+ */
+public class ClassifierValidator extends AbstractValidator<Classifier> {
+
+ private static final String SRC_IP = "classifer.srcIp";
+ private static final String SRC_PORT = "classifer.srcPort";
+
+ private static final String DST_IP = "classifer.dstIp";
+ private static final String DST_PORT = "classifer.dstPort";
+
+ private static final String TOS_BYTE = "classifer.tos-byte";
+ private static final String TOS_MASK = "classifer.tos-mask";
+
+ private static final String PROTOCOL = "classifer.protocol";
+
+ @Override
+ public void validate(final Classifier classifier, final Extent extent) throws ValidationException {
+
+ if (classifier == null) {
+ throw new ValidationException("classifer must exist");
+ }
+
+ mustExist(classifier.getSrcIp(), SRC_IP);
+ mustExist(classifier.getSrcPort(), SRC_PORT);
+
+ mustExist(classifier.getDstIp(), DST_IP);
+ mustExist(classifier.getDstPort(), DST_PORT);
+
+ mustExist(classifier.getTosByte(), TOS_BYTE);
+ mustExist(classifier.getTosMask(), TOS_MASK);
+
+ mustExist(classifier.getProtocol(), PROTOCOL);
+
+ throwErrorsIfNeeded();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.ext.classifier.ExtClassifier;
+
+/**
+ * @author rvail
+ */
+public class ExtClassifierValidator extends AbstractValidator<ExtClassifier> {
+
+ private static final String SRC_IP = "ext-classifer.srcIp";
+ private static final String SRC_MASK = "ext-classifer.srcIpMask";
+
+ private static final String DST_IP = "ext-classifer.dstIp";
+ private static final String DST_MASK = "ext-classifer.dstIpMask";
+
+ private static final String TOS_BYTE = "ext-classifer.tos-byte";
+ private static final String TOS_MASK = "ext-classifer.tos-mask";
+
+ private static final String PROTOCOL = "ext-classifer.protocol";
+
+ private static final String SRC_PORT_START = "ext-classifer.srcPort-start";
+ private static final String SRC_PORT_END = "ext-classifer.srcPort-end";
+
+ private static final String DST_PORT_START = "ext-classifer.dstPort-start";
+ private static final String DST_PORT_END = "ext-classifer.dstPort-end";
+
+ @Override
+ public void validate(final ExtClassifier extClassifier, final Extent extent) throws ValidationException {
+
+ mustExist(extClassifier.getSrcIp(), SRC_IP);
+ mustExist(extClassifier.getSrcIpMask(), SRC_MASK);
+
+ mustExist(extClassifier.getDstIp(), DST_IP);
+ mustExist(extClassifier.getDstIpMask(), DST_MASK);
+
+ mustExist(extClassifier.getTosByte(), TOS_BYTE);
+ mustExist(extClassifier.getTosMask(), TOS_MASK);
+
+ mustExist(extClassifier.getProtocol(), PROTOCOL);
+
+ mustExist(extClassifier.getSrcPortStart(), SRC_PORT_START);
+ mustExist(extClassifier.getSrcPortEnd(), SRC_PORT_END);
+
+ mustExist(extClassifier.getDstPortStart(), DST_PORT_START);
+ mustExist(extClassifier.getDstPortEnd(), DST_PORT_END);
+
+ throwErrorsIfNeeded();
+ }
+}
--- /dev/null
+/*
+ * Copyright (c) 2015 CableLabs and others. All rights reserved.
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License v1.0 which accompanies this distribution,
+ * and is available at http://www.eclipse.org/legal/epl-v10.html
+ */
+
+package org.opendaylight.controller.packetcable.provider.validation.impl.validators.qos.classifier;
+
+import org.opendaylight.controller.packetcable.provider.validation.ValidationException;
+import org.opendaylight.controller.packetcable.provider.validation.impl.validators.AbstractValidator;
+import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.pcmm.qos.ipv6.classifier.Ipv6Classifier;
+
+/**
+ * @author rvail
+ */
+public class Ipv6ClassifierValidator extends AbstractValidator<Ipv6Classifier> {
+
+
+ private static final String SRC_IP6 = "ipv6-classifer.srcIp6";
+ private static final String DST_IP6 = "ipv6-classifer.dstIp6";
+
+ private static final String TC_LOW = "ipv6-classifer.tc-low";
+ private static final String TC_HIGH = "ipv6-classifer.tc-high";
+ private static final String TC_MASK = "ipv6-classifer.tc-mask";
+
+ private static final String NEXT_HEADER = "ipv6-classifer.next-hdr";
+
+ private static final String FLOW_LABEL = "ipv6-classifer.flow-label";
+
+ private static final String SRC_PORT_START = "ipv6-classifer.srcPort-start";
+ private static final String SRC_PORT_END = "ipv6-classifer.srcPort-end";
+
+ private static final String DST_PORT_START = "ipv6-classifer.dstPort-start";
+ private static final String DST_PORT_END = "ipv6-classifer.dstPort-end";
+
+ @Override
+ public void validate(final Ipv6Classifier ipv6Classifier, final Extent extent) throws ValidationException {
+ if (ipv6Classifier == null) {
+ throw new ValidationException("ipv6-classifer must exist");
+ }
+
+ mustExist(ipv6Classifier.getSrcIp6(), SRC_IP6);
+ mustExist(ipv6Classifier.getDstIp6(), DST_IP6);
+
+ mustExist(ipv6Classifier.getTcLow(), TC_LOW);
+ mustExist(ipv6Classifier.getTcHigh(), TC_HIGH);
+ mustExist(ipv6Classifier.getTcMask(), TC_MASK);
+
+ mustExist(ipv6Classifier.getNextHdr(), NEXT_HEADER);
+
+ mustExist(ipv6Classifier.getFlowLabel(), FLOW_LABEL);
+
+ mustExist(ipv6Classifier.getSrcPortStart(), SRC_PORT_START);
+ mustExist(ipv6Classifier.getSrcPortEnd(), SRC_PORT_END);
+
+ mustExist(ipv6Classifier.getDstPortStart(), DST_PORT_START);
+ mustExist(ipv6Classifier.getDstPortEnd(), DST_PORT_END);
+
+ throwErrorsIfNeeded();
+ }
+}
final String expGateSetMsgStart) {
final Gate gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
- final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
- Assert.assertNotNull(gateSetMsg);
- Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
+// final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
+// Assert.assertNotNull(gateSetMsg);
+// Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
+
+ // TODO update this method for the new GateSetStatus object
+ PCMMService.GateSetStatus status = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
+ Assert.assertNotNull(status);
+ Assert.assertTrue(status.getMessage().startsWith(expGateSetMsgStart));
// TODO - add validation to the PCMMGateReq contained within the map
Assert.assertNotNull(service.gateRequests.get(gatePath));
import java.net.InetAddress;
import java.util.concurrent.ExecutionException;
-
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
-import org.opendaylight.controller.packetcable.provider.PacketcableProvider;
@RunWith(MockitoJUnitRunner.class)
public class PacketcableProviderTest {
package org.opendaylight.controller.packetcable.provider;
+import static org.hamcrest.CoreMatchers.equalTo;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.CoreMatchers.not;
+import static org.hamcrest.CoreMatchers.notNullValue;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+
import com.google.common.net.InetAddresses;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
-import java.net.InetAddress;
-import java.net.UnknownHostException;
-
-import static org.hamcrest.CoreMatchers.*;
-import static org.junit.Assert.*;
-
public class SubnetTest {
// Various address class level prefix lengths