From d42d25f6c01dfc742f4410cf809a306e59242c86 Mon Sep 17 00:00:00 2001 From: Ryan Vail Date: Thu, 29 Oct 2015 10:26:47 -0600 Subject: [PATCH 1/1] Added response data to operational datastore, refactored data validation Update yang file to have response data in operation datastore Response data for ccap and qos/gate data added. Validation code refactored into several classes to make maintenance and testing easier (Tests not yet written) Extended MdsalUtils with read, put methods Created AbstractDataChangeListener to help with new incoming data Divided DataChangeListiing into two classes one for packetcable:ccaps and one for packetcable:qos Added a few new items to the postman collection with operational DS fetches and bad puts for testing Change-Id: I4054bbbf893476327fc39610a47bfa564024610a Signed-off-by: Ryan Vail --- packetcable-driver/pom.xml | 1 + packetcable-policy-model/pom.xml | 4 + .../src/main/yang/packetcable.yang | 153 ++- .../ODL-PCMM.json.postman_collection | 94 +- .../provider/AbstractDataChangeListener.java | 116 +++ .../packetcable/provider/DataChangeUtils.java | 119 +++ .../packetcable/provider/MdsalUtils.java | 58 +- .../provider/PCMMGateReqBuilder.java | 30 +- .../packetcable/provider/PCMMService.java | 532 +++++----- .../provider/PacketcableProvider.java | 975 ++++++++++++------ .../packetcable/provider/Subnet.java | 34 +- .../provider/ValidateInstanceData.java | 951 ----------------- .../provider/validation/DataValidator.java | 91 ++ .../validation/ValidationException.java | 54 + .../provider/validation/Validator.java | 23 + .../validation/ValidatorProvider.java | 66 ++ .../validation/ValidatorProviderFactory.java | 18 + .../impl/CcapsValidatorProviderFactory.java | 42 + .../impl/QosValidatorProviderFactory.java | 64 ++ .../impl/ValidatorProviderFactoryImpl.java | 30 + .../impl/ValidatorProviderImpl.java | 77 ++ .../impl/validators/AbstractValidator.java | 102 ++ .../impl/validators/ccaps/AmIdValidator.java | 35 + .../impl/validators/ccaps/CcapValidator.java | 52 + .../impl/validators/ccaps/CcapsValidator.java | 36 + .../validators/ccaps/ConnectionValidator.java | 37 + .../impl/validators/qos/AppValidator.java | 40 + .../impl/validators/qos/AppsValidator.java | 37 + .../validators/qos/GateSpecValidatator.java | 36 + .../impl/validators/qos/GateValidator.java | 90 ++ .../impl/validators/qos/GatesValidator.java | 37 + .../validators/qos/SubscriberValidator.java | 40 + .../validators/qos/SubscribersValidator.java | 38 + .../qos/TrafficProfileValidator.java | 33 + .../qos/classifier/ClassifierValidator.java | 51 + .../classifier/ExtClassifierValidator.java | 59 ++ .../classifier/Ipv6ClassifierValidator.java | 63 ++ .../packetcable/provider/PCMMServiceTest.java | 11 +- .../provider/PacketcableProviderTest.java | 2 - .../packetcable/provider/SubnetTest.java | 17 +- 40 files changed, 2765 insertions(+), 1583 deletions(-) create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/AbstractDataChangeListener.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/DataChangeUtils.java delete mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/ValidateInstanceData.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/DataValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidationException.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/Validator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidatorProvider.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidatorProviderFactory.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/CcapsValidatorProviderFactory.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/QosValidatorProviderFactory.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/ValidatorProviderFactoryImpl.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/ValidatorProviderImpl.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/AbstractValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/AmIdValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/CcapValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/CcapsValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/ConnectionValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/AppValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/AppsValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GateSpecValidatator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GateValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GatesValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/SubscriberValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/SubscribersValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/TrafficProfileValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/ClassifierValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/ExtClassifierValidator.java create mode 100644 packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/Ipv6ClassifierValidator.java diff --git a/packetcable-driver/pom.xml b/packetcable-driver/pom.xml index d7bf8ac..d6a1024 100644 --- a/packetcable-driver/pom.xml +++ b/packetcable-driver/pom.xml @@ -7,6 +7,7 @@ org.opendaylight.packetcable packetcable 1.3.0-SNAPSHOT + .. packetcable-driver bundle diff --git a/packetcable-policy-model/pom.xml b/packetcable-policy-model/pom.xml index 03c9b58..e1d2154 100644 --- a/packetcable-policy-model/pom.xml +++ b/packetcable-policy-model/pom.xml @@ -39,6 +39,10 @@ org.opendaylight.mdsal yang-binding + + org.opendaylight.mdsal.model + yang-ext + org.opendaylight.yangtools yang-common diff --git a/packetcable-policy-model/src/main/yang/packetcable.yang b/packetcable-policy-model/src/main/yang/packetcable.yang index d9562f4..22be5d5 100644 --- a/packetcable-policy-model/src/main/yang/packetcable.yang +++ b/packetcable-policy-model/src/main/yang/packetcable.yang @@ -3,12 +3,13 @@ module packetcable 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"; } @@ -44,6 +45,13 @@ module packetcable 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 { @@ -54,14 +62,58 @@ module packetcable 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, @@ -69,24 +121,18 @@ module packetcable 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 { @@ -98,6 +144,11 @@ module packetcable leaf-list downstream-scns { type service-class-name; } + leaf-list error { + config false; + type string; + description "ccap data errors"; + } } // PCMM QoS Gates @@ -123,6 +174,7 @@ module packetcable container apps { list app { key "appId"; + ext:context-instance "app-context"; leaf appId { type string; description "Application Identity"; @@ -132,7 +184,8 @@ module packetcable 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 { @@ -140,7 +193,9 @@ module packetcable leaf gateId { type string; description "Qos Gate Identity"; + mandatory true; } + uses gate-operational-attributes; uses pcmm-qos-gate-attributes; } } @@ -150,6 +205,44 @@ module packetcable } } + 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; @@ -180,6 +273,7 @@ module packetcable 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; } } } @@ -188,18 +282,22 @@ module packetcable 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; } } @@ -208,30 +306,37 @@ module packetcable 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; } } } @@ -241,30 +346,37 @@ module packetcable 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; } @@ -274,33 +386,40 @@ module packetcable container ipv6-classifier { leaf srcIp6 { type inet:ipv6-prefix; - description "Source IPv6 prefix match in
notation"; + description "Source IPv6 prefix match in 'address/len' notation"; + mandatory true; } leaf dstIp6 { type inet:ipv6-prefix; - description "Destination IPv6 prefix match in
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; } diff --git a/packetcable-policy-server/doc/restconf-samples/ODL-PCMM.json.postman_collection b/packetcable-policy-server/doc/restconf-samples/ODL-PCMM.json.postman_collection index 98b9d7e..3b934c8 100644 --- a/packetcable-policy-server/doc/restconf-samples/ODL-PCMM.json.postman_collection +++ b/packetcable-policy-server/doc/restconf-samples/ODL-PCMM.json.postman_collection @@ -9,14 +9,14 @@ "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 }, @@ -25,6 +25,9 @@ "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", @@ -34,10 +37,11 @@ "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, @@ -77,12 +81,12 @@ "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", @@ -239,6 +243,45 @@ "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", @@ -335,6 +378,45 @@ "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", diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/AbstractDataChangeListener.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/AbstractDataChangeListener.java new file mode 100644 index 0000000..b099471 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/AbstractDataChangeListener.java @@ -0,0 +1,116 @@ +/* + * 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 implements DataChangeListener { + + protected final Logger logger = LoggerFactory.getLogger(getClass()); + + private final Class tClass; + + public AbstractDataChangeListener(Class tClass) { + this.tClass = checkNotNull(tClass); + } + + @Override + public void onDataChanged(final AsyncDataChangeEvent, DataObject> asyncDataChangeEvent) { + logger.debug("############{}.onDataChanged()", getClass().getSimpleName()); + logChange(logger, asyncDataChangeEvent); + + final Map, DataObject> allCreatedData = asyncDataChangeEvent.getCreatedData(); + final Map, DataObject> allOriginalData = asyncDataChangeEvent.getOriginalData(); + final Map, DataObject> allUpdatedData = asyncDataChangeEvent.getUpdatedData(); + + // UpdatedData also contains all data that was created, remove it to get the set of only updated data + final Map, DataObject> trueUpdatedData = + relativeComplement(allCreatedData, allUpdatedData); + final Map, DataObject> trueOriginalData = + relativeComplement(allCreatedData, allOriginalData); + + if (!allCreatedData.isEmpty()) { + final Map, 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, 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, T> originalTs = collectTypeFromMap(tClass, trueOriginalData); + for (InstanceIdentifier iid : updatedTs.keySet()) { + if (!originalTs.containsKey(iid)) { + logger.warn("No original data for updated object {}", iid); + } + } + + handleUpdatedData(updatedTs, originalTs); + } + } + + final Set> allRemovedPaths = asyncDataChangeEvent.getRemovedPaths(); + if (!allRemovedPaths.isEmpty()) { + final Set> 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, T> originalTData = Maps.newHashMapWithExpectedSize(removedTPaths.size()); + for (InstanceIdentifier iid : removedTPaths) { + if (allOriginalData.containsKey(iid)) { + + originalTData.put(iid, (T) allOriginalData.get(iid)); + } + } + + handleRemovedData(removedTPaths, originalTData); + } + } + + protected abstract void handleCreatedData(final Map, T> createdData); + + protected abstract void handleUpdatedData(final Map, T> updatedData, + final Map, T> originalData); + + protected abstract void handleRemovedData(final Set> removedPaths, + final Map, T> originalData); + + + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/DataChangeUtils.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/DataChangeUtils.java new file mode 100644 index 0000000..ceac675 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/DataChangeUtils.java @@ -0,0 +1,119 @@ +/* + * 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 Map, T> collectTypeFromMap(Class tClass, + Map, DataObject> map) { + Map, T> result = Maps.newHashMap(); + + for (Map.Entry, DataObject> entry : map.entrySet()) { + if (tClass.isAssignableFrom(entry.getValue().getImplementedInterface())) { + final InstanceIdentifier tIID = (InstanceIdentifier) entry.getKey(); + final T tObj = (T) entry.getValue(); + result.put(tIID, tObj); + } + } + + return result; + } + + public static Set> collectTypeFromSet(Class tClass, + Set> set) { + Set> result = Sets.newHashSet(); + + for (InstanceIdentifier iid : set) { + if (tClass.isAssignableFrom(iid.getTargetType())) { + @SuppressWarnings("unchecked") + final InstanceIdentifier tIID = (InstanceIdentifier) 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, DataObject> relativeComplement(Map, DataObject> setA, + Map, DataObject> setB){ + + Map, 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, 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 void appendSet(StringBuilder sb, String name, Set 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, DataObject> dataMap) { + sb.append("====").append(name).append("====\n"); + if (dataMap == null || dataMap.isEmpty()) { + sb.append("None\n"); + } else { + for (Map.Entry, DataObject> entry : dataMap.entrySet()) { + sb.append(entry.getValue()).append("\n"); + sb.append("----------\n"); + } + } + sb.append("===============\n"); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/MdsalUtils.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/MdsalUtils.java index 8f32d0d..04739ad 100644 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/MdsalUtils.java +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/MdsalUtils.java @@ -8,16 +8,19 @@ 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; @@ -25,7 +28,7 @@ public class MdsalUtils { /** * 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; @@ -39,7 +42,7 @@ public class MdsalUtils { * @param the data object type * @return the result of the request */ - public boolean delete( + public boolean delete( final LogicalDatastoreType store, final InstanceIdentifier path) { boolean result = false; final WriteTransaction transaction = databroker.newWriteOnlyTransaction(); @@ -62,7 +65,7 @@ public class MdsalUtils { * @param the data object type * @return the result of the request */ - public boolean merge( + public boolean merge( final LogicalDatastoreType logicalDatastoreType, final InstanceIdentifier path, D data) { boolean result = false; final WriteTransaction transaction = databroker.newWriteOnlyTransaction(); @@ -76,4 +79,49 @@ public class MdsalUtils { } return result; } + + /** + * Execures a read as a blocking transaction. + * + * @param logicalDatastoreType which datastore to read from + * @param path The path to read + * @param the DataObject type + * @return an Optional containing the object. + */ + public Optional read(final LogicalDatastoreType logicalDatastoreType, final InstanceIdentifier path) { + final ReadOnlyTransaction readOnlyTransaction = databroker.newReadOnlyTransaction(); + + CheckedFuture, 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 the data object type + * @return the result of the request + */ + public boolean put( + final LogicalDatastoreType logicalDatastoreType, final InstanceIdentifier path, D data) { + boolean result = false; + final WriteTransaction transaction = databroker.newWriteOnlyTransaction(); + transaction.put(logicalDatastoreType, path, data, true); + CheckedFuture future = transaction.submit(); + try { + future.checkedGet(); + result = true; + } catch (TransactionCommitFailedException e) { + LOG.warn("Failed to merge {} ", path, e); + } + return result; + } } diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMGateReqBuilder.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMGateReqBuilder.java index a06702d..83a0380 100644 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMGateReqBuilder.java +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMGateReqBuilder.java @@ -1,8 +1,17 @@ -/** - * 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; @@ -17,21 +26,22 @@ import org.pcmm.gates.IExtendedClassifier.ActivationState; 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; diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java index 09f481b..7e31a9c 100644 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java @@ -1,7 +1,10 @@ /* - * (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; @@ -29,239 +32,296 @@ import org.umu.cops.stack.COPSError.ErrorTypes; */ @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 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 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; + } + } } diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PacketcableProvider.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PacketcableProvider.java index 894eccc..bbd9603 100644 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PacketcableProvider.java +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PacketcableProvider.java @@ -1,20 +1,42 @@ +/* + * 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; @@ -23,14 +45,22 @@ import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.Ccaps; 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; @@ -41,28 +71,18 @@ import org.slf4j.LoggerFactory; /** * Called by ODL framework to start this bundle. - * + *

* 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 ccapIID = InstanceIdentifier.builder(Ccaps.class).build(); - public static final InstanceIdentifier qosIID = InstanceIdentifier.builder(Qos.class).build(); - - /** - * The ODL object used to broker messages throughout the framework - */ - private DataBroker dataBroker; - - private MdsalUtils mdsalUtils; - - private ListenerRegistration ccapDataChangeListenerRegistration; - private ListenerRegistration qosDataChangeListenerRegistration; + // keys to the /restconf/config/packetcable:ccaps and /restconf/config/packetcable:qos config datastore + private static final InstanceIdentifier ccapsIID = InstanceIdentifier.builder(Ccaps.class).build(); + private static final InstanceIdentifier qosIID = InstanceIdentifier.builder(Qos.class).build(); // TODO - Revisit these maps and remove the ones no longer necessary private final Map ccapMap = new ConcurrentHashMap<>(); @@ -72,11 +92,26 @@ public class PacketcableProvider implements BindingAwareProvider, DataChangeList private final Map> downstreamScnMap = new ConcurrentHashMap<>(); private final Map> upstreamScnMap = new ConcurrentHashMap<>(); + private final Executor executor = Executors.newSingleThreadExecutor(); + /** * Holds a PCMMService object for each CCAP being managed. */ private final Map 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 ccapsDataChangeListenerRegistration; + private ListenerRegistration qosDataChangeListenerRegistration; + /** * Constructor */ @@ -87,26 +122,51 @@ public class PacketcableProvider implements BindingAwareProvider, DataChangeList @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 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) { @@ -114,24 +174,6 @@ public class PacketcableProvider implements BindingAwareProvider, DataChangeList } } - 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()) { @@ -163,33 +205,22 @@ public class PacketcableProvider implements BindingAwareProvider, DataChangeList } } - private void removeCcapFromAllMaps(final Ccap ccap) { - // remove the ccap from all maps - // subscriberSubnets map - for (final Map.Entry entry : subscriberSubnetsMap.entrySet()) { - if (entry.getValue() == ccap) { - subscriberSubnetsMap.remove(entry.getKey()); - } - } - // ccap to upstream SCN map - for (final Map.Entry> entry : upstreamScnMap.entrySet()) { - final List ccapList = entry.getValue(); - ccapList.remove(ccap); - if (ccapList.isEmpty()) { - upstreamScnMap.remove(entry.getKey()); - } - } - // ccap to downstream SCN map - for (final Map.Entry> entry : downstreamScnMap.entrySet()) { - final List 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) { @@ -209,6 +240,9 @@ public class PacketcableProvider implements BindingAwareProvider, DataChangeList } private ServiceFlowDirection findScnOnCcap(final ServiceClassName scn, final Ccap ccap) { + checkNotNull(scn); + checkNotNull(ccap); + if (upstreamScnMap.containsKey(scn)) { final List ccapList = upstreamScnMap.get(scn); if (ccapList.contains(ccap)) { @@ -223,313 +257,586 @@ public class PacketcableProvider implements BindingAwareProvider, DataChangeList return null; } + private void removeCcapFromAllMaps(final Ccap ccap) { + // remove the ccap from all maps + // subscriberSubnets map + for (final Map.Entry entry : subscriberSubnetsMap.entrySet()) { + if (entry.getValue() == ccap) { + subscriberSubnetsMap.remove(entry.getKey()); + } + } + // ccap to upstream SCN map + for (final Map.Entry> entry : upstreamScnMap.entrySet()) { + final List ccapList = entry.getValue(); + ccapList.remove(ccap); + if (ccapList.isEmpty()) { + upstreamScnMap.remove(entry.getKey()); + } + } + // ccap to downstream SCN map + for (final Map.Entry> entry : downstreamScnMap.entrySet()) { + final List 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 void saveErrors(@Nonnull Map, ValidationException> errorMap, + @Nonnull Map, T> dataMap) { + + final WriteTransaction writeTransaction = dataBroker.newWriteOnlyTransaction(); + + + for (InstanceIdentifier iid : errorMap.keySet()) { + + final ValidationException exception = errorMap.get(iid); + final T badData = dataMap.get(iid); + + if (!badData.getImplementedInterface().isAssignableFrom(iid.getTargetType())) { + // InstanceIdentifier 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 ccapIID = (InstanceIdentifier) 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 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 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 { - private class InstanceData { - // CCAP Identity - public final Map, Ccap> ccapIidMap = new HashMap<>(); - // Gate Identity - public String subId; - public final Map gatePathMap = new HashMap<>(); - public String gatePath; - public final Map, Gate> gateIidMap = new HashMap<>(); - // remove path for either CCAP or Gate - public final Set removePathList = new HashSet<>(); - - public final Set> reqCcapIds = new HashSet<>(); - - public InstanceData(final Map, 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> 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 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 appInstance = thisInstance.firstIdentifierOf(App.class); - if (appInstance != null) { - final AppKey appKey = InstanceIdentifier.keyOf(appInstance); - if (appKey != null) { - gatePathMap.put("appId", appKey.getAppId()); - } - } - final InstanceIdentifier 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 { + + public SubscriberCleaner(InstanceIdentifier removedGateIID) { + super(removedGateIID, Subscriber.class, LogicalDatastoreType.OPERATIONAL); + } + + @Override + protected boolean shouldClean(final Subscriber subscriber) { + return subscriber.getGates().getGate().isEmpty(); + } + + @Override + protected void postRemove(InstanceIdentifier subscriberIID) { + executor.execute(new AppCleaner(subscriberIID)); + } + } + + + /** + * Removes App if all Subscribers are removed. + */ + private class AppCleaner extends AbstractCleaner { + + public AppCleaner(InstanceIdentifier removedSubscriberIID) { + super(removedSubscriberIID, App.class, LogicalDatastoreType.OPERATIONAL); + } + + @Override + boolean shouldClean(final App app) { + return app.getSubscribers().getSubscriber().isEmpty(); + } + + @Override + void postRemove(final InstanceIdentifier appIID) { + executor.execute(new AppsCleaner(appIID)); + } + } + + + /** + * Removes Apps if all App instances are removed. + */ + private class AppsCleaner extends AbstractCleaner { + + public AppsCleaner(InstanceIdentifier 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)}.
+ * + * Subclasses can react after an instance is removed by overriding {@link #postRemove(InstanceIdentifier)} + * @param The type that will be removed + */ + private abstract class AbstractCleaner implements Runnable { + final InstanceIdentifier removedIID; + final Class tClass; + final LogicalDatastoreType datastoreType; + + public AbstractCleaner(InstanceIdentifier removedIID, Class tClass, LogicalDatastoreType datastoreType) { + this.removedIID = checkNotNull(removedIID); + this.tClass = checkNotNull(tClass); + this.datastoreType = checkNotNull(datastoreType); + } + + @Override + public void run() { + InstanceIdentifier tIID = removedIID.firstIdentifierOf(tClass); + if (tIID != null) { + Optional optional = mdsalUtils.read(datastoreType, tIID); + if (optional.isPresent()) { + + if (shouldClean(optional.get())) { + if (mdsalUtils.delete(datastoreType, tIID)) { + postRemove(tIID); } - } - final InstanceIdentifier 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, DataObject> thisData) { - logger.info("onDataChanged().getCcaps(): " + thisData); - for (final Map.Entry, 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 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 tIID) { - if (entry.getKey().getTargetType().equals(Connection.class) || - entry.getKey().getTargetType().equals(Ccap.class)) { - reqCcapIds.add(entry.getKey()); - } - } } - private void getGates(final Map, DataObject> thisData) { - logger.info("onDataChanged().getGates(): " + thisData); - for (final Map.Entry, DataObject> entry : thisData.entrySet()) { - if (entry.getValue() instanceof Gate) { - final Gate gate = (Gate)entry.getValue(); - - // TODO FIXME - Potential ClassCastException thrown here!!! - final InstanceIdentifier gateIID = (InstanceIdentifier)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 gateIID = (InstanceIdentifier)entry.getKey(); -// getGatePathMap(gateIID); -// if (!gateIidMap.containsKey(gateIID)){ -// gateIidMap.put(gateIID, gates); -// } -// } -// } -// } -// } -// } -// } -// } - } + void removeFailed(InstanceIdentifier tIID) { + logger.error("Failed to remove {}", tIID); } } - @Override - public void onDataChanged(final AsyncDataChangeEvent, 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 { + + private final DataValidator ccapsDataValidator = new DataValidator(new CcapsValidatorProviderFactory().build()); + + private final Set> updateQueue = Sets.newConcurrentHashSet(); + + public CcapsDataChangeListener() { + super(Ccap.class); + } + + @Override + protected void handleCreatedData(final Map, 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, 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, Ccap> goodData = + Maps.newHashMapWithExpectedSize(createdCcaps.size() - errorMap.size()); + for (InstanceIdentifier 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, 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, Ccap> goodData) { + for (InstanceIdentifier 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.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, 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 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, Ccap> updatedCcaps, + final Map, Ccap> originalCcaps) { + + // TODO actually support updates + + // update operation not allowed -- restore the original config object and complain + for (final Map.Entry, 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> removedCcapPaths, + final Map, Ccap> originalCcaps) { + + for (InstanceIdentifier 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 { + + private final DataValidator qosDataValidator = new DataValidator(new QosValidatorProviderFactory().build()); + private final Set> updateQueue = Sets.newConcurrentHashSet(); + + public QosDataChangeListener() { + super(Gate.class); + } + + @Override + protected void handleCreatedData(final Map, Gate> createdData) { + + final Map, 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, Gate> goodData = + Maps.newHashMapWithExpectedSize(createdData.size() - errorMap.size()); + for (InstanceIdentifier 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, Gate> createdGates) { + + for (InstanceIdentifier gateIID : createdGates.keySet()) { + final Gate newGate = createdGates.get(gateIID); + + final String newGatePathStr = makeGatePathString(gateIID); + + final InstanceIdentifier 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, 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 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, Gate> updatedData, + final Map, Gate> originalData) { + // TODO actually support updates + + // update operation not allowed -- restore the original config object and complain + for (final Map.Entry, 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, 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> removedPaths, + final Map, Gate> originalData) { + + for (final InstanceIdentifier 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 iid) { + final InstanceIdentifier appIID = iid.firstIdentifierOf(App.class); + final AppKey appKey = InstanceIdentifier.keyOf(appIID); + + final InstanceIdentifier subscriberIID = iid.firstIdentifierOf(Subscriber.class); + final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(subscriberIID); + + final GateKey gateKey = InstanceIdentifier.keyOf(iid); + + return appKey.getAppId() + + "/" + subscriberKey.getSubscriberId() + + "/" + gateKey.getGateId(); } } + } diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/Subnet.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/Subnet.java index b8caf65..1c6b015 100644 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/Subnet.java +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/Subnet.java @@ -1,15 +1,23 @@ +/* + * 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 @@ -17,13 +25,13 @@ import static com.google.common.base.Preconditions.checkNotNull; 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; @@ -154,7 +162,7 @@ public class Subnet } @Override - final public boolean equals( Object obj ) + public final boolean equals( Object obj ) { if (null == obj) return false; if (this == obj) return true; @@ -168,7 +176,7 @@ public class Subnet } @Override - final public int hashCode() + public final int hashCode() { return new HashCodeBuilder(997, 311) .append(prefixLen) @@ -188,7 +196,7 @@ public class Subnet 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(); @@ -211,4 +219,4 @@ public class Subnet buf.append( isIPv4 ? integer : Integer.toHexString( integer ) ); } } -} \ No newline at end of file +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/ValidateInstanceData.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/ValidateInstanceData.java deleted file mode 100644 index 49afb8c..0000000 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/ValidateInstanceData.java +++ /dev/null @@ -1,951 +0,0 @@ -/** - * 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, Gate> gateIidMap; - - // CCAP Identity - private transient Ccap ccap; - private transient InstanceIdentifier ccapIID; - - public ValidateInstanceData(final MdsalUtils mdsalUtils, final Map, 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, Gate> entry : gateIidMap.entrySet()) { - InstanceIdentifier 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, DataObject> thisData) { - for (final Map.Entry, DataObject> entry : thisData.entrySet()) { - if (entry.getValue() instanceof Ccap) { - ccap = (Ccap)entry.getValue(); - // TODO FIXME - ClassCastException waiting to occur here!!! - ccapIID = (InstanceIdentifier) entry.getKey(); - } - } - } - - private void getGates(final Map, DataObject> thisData) { - for (final Map.Entry, DataObject> entry : thisData.entrySet()) { - if (entry.getValue() instanceof Gate) { - final Gate gate = (Gate)entry.getValue(); - // TODO FIXME - ClassCastException waiting to occur here!!! - final InstanceIdentifier gateIID = (InstanceIdentifier)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 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 / 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 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 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); - } -} - diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/DataValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/DataValidator.java new file mode 100644 index 0000000..906ce9a --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/DataValidator.java @@ -0,0 +1,91 @@ +/* + * 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, ValidationException> validate( + @Nonnull final Map, DataObject> dataObjectMap, + @Nonnull final Validator.Extent extent) { + checkNotNull(dataObjectMap); + + Map, ValidationException> exceptionMap = Maps.newHashMap(); + + for (Map.Entry, 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 Map, ValidationException> validateOneType( + @Nonnull final Map, T> dataObjectMap, @Nonnull final Validator.Extent extent) { + checkNotNull(dataObjectMap); + + Map, ValidationException> exceptionMap = Maps.newHashMap(); + + for (Map.Entry, T> entry : dataObjectMap.entrySet()) { + final InstanceIdentifier 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; + } + + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidationException.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidationException.java new file mode 100644 index 0000000..36bcaac --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidationException.java @@ -0,0 +1,54 @@ +/* + * 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 errorMessages; + + public ValidationException(final String... errorMessages) { + super(concat(Arrays.asList(errorMessages))); + this.errorMessages = ImmutableList.copyOf(errorMessages); + } + + private static String concat(Collection strings) { + checkNotNull(strings); + + final Iterator 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 errorMessages) { + super(concat(errorMessages)); + this.errorMessages = ImmutableList.copyOf(errorMessages); + } + + public ImmutableList getErrorMessages() { + return errorMessages; + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/Validator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/Validator.java new file mode 100644 index 0000000..219acef --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/Validator.java @@ -0,0 +1,23 @@ +/* + * 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 { + + void validate(final T data, Extent extent) throws ValidationException; + + enum Extent { + NODE_ONLY, + NODE_AND_SUBTREE + } + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidatorProvider.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidatorProvider.java new file mode 100644 index 0000000..20d1d25 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidatorProvider.java @@ -0,0 +1,66 @@ +/* + * 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 + * The type being validated + */ + void put(@Nonnull final Class tClass, @Nonnull final Validator validator); + + /** + * Gets the validator for a particular type + * + * @param tClass + * The Class of T + * @param + * 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. + */ + Validator validatorFor(@Nonnull final Class 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 + * The type of tClass + * @throws ValidationException + * if validation fails + * @throws IllegalArgumentException + * if data is not assignable from tClass + */ + void validate(@Nonnull final Class tClass, @Nonnull final DataObject data, + @Nonnull final Validator.Extent extent) throws ValidationException; +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidatorProviderFactory.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidatorProviderFactory.java new file mode 100644 index 0000000..23bf493 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/ValidatorProviderFactory.java @@ -0,0 +1,18 @@ +/* + * 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(); + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/CcapsValidatorProviderFactory.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/CcapsValidatorProviderFactory.java new file mode 100644 index 0000000..82ad94b --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/CcapsValidatorProviderFactory.java @@ -0,0 +1,42 @@ +/* + * 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; + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/QosValidatorProviderFactory.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/QosValidatorProviderFactory.java new file mode 100644 index 0000000..f8e541e --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/QosValidatorProviderFactory.java @@ -0,0 +1,64 @@ +/* + * 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; + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/ValidatorProviderFactoryImpl.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/ValidatorProviderFactoryImpl.java new file mode 100644 index 0000000..a013a54 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/ValidatorProviderFactoryImpl.java @@ -0,0 +1,30 @@ +/* + * 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; + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/ValidatorProviderImpl.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/ValidatorProviderImpl.java new file mode 100644 index 0000000..c1c8f57 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/ValidatorProviderImpl.java @@ -0,0 +1,77 @@ +/* + * 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, Validator> validatorMap; + + public ValidatorProviderImpl() { + this.validatorMap = Maps.newHashMap(); + } + + /** + * {@inheritDoc} + */ + @Override + public void put(@Nonnull final Class tClass, @Nonnull final Validator validator) { + checkNotNull(tClass); + checkNotNull(validator); + validatorMap.put(tClass, validator); + } + + /** + * {@inheritDoc} + */ + @Override + public void validate(@Nonnull final Class 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 Validator validatorFor(@Nonnull final Class 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 result = (Validator) validatorMap.get(tClass); + return result; + } + throw new NoSuchElementException("Entry not found for key: " + tClass); + } + + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/AbstractValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/AbstractValidator.java new file mode 100644 index 0000000..b2bd654 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/AbstractValidator.java @@ -0,0 +1,102 @@ +/* + * 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.
+ * This delays array creation until bad data is found. + *

+ * Subclasses must call {@link #throwErrorsIfNeeded()} at the end of validate() + * + * @author rvail + */ +@NotThreadSafe +public abstract class AbstractValidator implements Validator { + + private ArrayList 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 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 void validateChild(Validator validator, C child) { + try { + validator.validate(child, Extent.NODE_AND_SUBTREE); + } catch (ValidationException e) { + getErrorMessages().addAll(e.getErrorMessages()); + } + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/AmIdValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/AmIdValidator.java new file mode 100644 index 0000000..c442179 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/AmIdValidator.java @@ -0,0 +1,35 @@ +/* + * 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 { + + 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(); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/CcapValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/CcapValidator.java new file mode 100644 index 0000000..faeb0c5 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/CcapValidator.java @@ -0,0 +1,52 @@ +/* + * 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 { + + 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(); + } + + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/CcapsValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/CcapsValidator.java new file mode 100644 index 0000000..535773d --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/CcapsValidator.java @@ -0,0 +1,36 @@ +/* + * 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 { + + 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); + } + } + } + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/ConnectionValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/ConnectionValidator.java new file mode 100644 index 0000000..1a737f3 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/ccaps/ConnectionValidator.java @@ -0,0 +1,37 @@ +/* + * 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 { + + 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(); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/AppValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/AppValidator.java new file mode 100644 index 0000000..e830786 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/AppValidator.java @@ -0,0 +1,40 @@ +/* + * 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 { + + 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(); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/AppsValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/AppsValidator.java new file mode 100644 index 0000000..dbf0633 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/AppsValidator.java @@ -0,0 +1,37 @@ +/* + * 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 { + + 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(); + } + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GateSpecValidatator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GateSpecValidatator.java new file mode 100644 index 0000000..7f1353b --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GateSpecValidatator.java @@ -0,0 +1,36 @@ +/* + * 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 { + + 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(); + } + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GateValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GateValidator.java new file mode 100644 index 0000000..e97974c --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GateValidator.java @@ -0,0 +1,90 @@ +/* + * 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 { + + 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(); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GatesValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GatesValidator.java new file mode 100644 index 0000000..a5eecaf --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/GatesValidator.java @@ -0,0 +1,37 @@ +/* + * 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 { + + 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(); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/SubscriberValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/SubscriberValidator.java new file mode 100644 index 0000000..fa1a2a4 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/SubscriberValidator.java @@ -0,0 +1,40 @@ +/* + * 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 { + + 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(); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/SubscribersValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/SubscribersValidator.java new file mode 100644 index 0000000..0bc8694 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/SubscribersValidator.java @@ -0,0 +1,38 @@ +/* + * 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 { + + 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(); + } + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/TrafficProfileValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/TrafficProfileValidator.java new file mode 100644 index 0000000..fb7a02d --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/TrafficProfileValidator.java @@ -0,0 +1,33 @@ +/* + * 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 { + + 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(); + } + +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/ClassifierValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/ClassifierValidator.java new file mode 100644 index 0000000..2a6ab77 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/ClassifierValidator.java @@ -0,0 +1,51 @@ +/* + * 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 { + + 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(); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/ExtClassifierValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/ExtClassifierValidator.java new file mode 100644 index 0000000..de8f02b --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/ExtClassifierValidator.java @@ -0,0 +1,59 @@ +/* + * 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 { + + 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(); + } +} diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/Ipv6ClassifierValidator.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/Ipv6ClassifierValidator.java new file mode 100644 index 0000000..87d4495 --- /dev/null +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/validation/impl/validators/qos/classifier/Ipv6ClassifierValidator.java @@ -0,0 +1,63 @@ +/* + * 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 { + + + 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(); + } +} diff --git a/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java b/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java index 8225e4d..4e3a9d4 100644 --- a/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java +++ b/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java @@ -340,9 +340,14 @@ public class PCMMServiceTest { 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)); diff --git a/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PacketcableProviderTest.java b/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PacketcableProviderTest.java index 64ff9a3..513f37b 100644 --- a/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PacketcableProviderTest.java +++ b/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PacketcableProviderTest.java @@ -8,13 +8,11 @@ import static org.mockito.Mockito.when; 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 { diff --git a/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/SubnetTest.java b/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/SubnetTest.java index b0360ad..5d52cd2 100644 --- a/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/SubnetTest.java +++ b/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/SubnetTest.java @@ -1,16 +1,21 @@ 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 -- 2.36.6