Cleanup tests.
[packetcable.git] / packetcable-policy-server / src / test / java / org / opendaylight / controller / packetcable / provider / PCMMServiceTest.java
1 /*
2  * (c) 2015 Cable Television Laboratories, Inc.  All rights reserved.
3  */
4
5 package org.opendaylight.controller.packetcable.provider;
6
7 import org.junit.After;
8 import org.junit.Assert;
9 import org.junit.Before;
10 import org.junit.Test;
11 import org.mockito.Mockito;
12 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
13 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
14 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
15 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.ServiceClassName;
16 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.ServiceFlowDirection;
17 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.TosByte;
18 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.TpProtocol;
19 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.ccap.Ccaps;
20 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.ccap.attributes.AmId;
21 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.ccap.attributes.Connection;
22 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.classifier.Classifier;
23 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.gate.spec.GateSpec;
24 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.gates.apps.subs.Gates;
25 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.traffic.profile.TrafficProfile;
26 import org.pcmm.PCMMPdpAgent;
27 import org.pcmm.gates.IGateSpec.Direction;
28 import org.pcmm.gates.IPCMMGate;
29 import org.pcmm.rcd.IPCMMClient;
30 import org.pcmm.rcd.impl.CMTS;
31 import org.umu.cops.stack.*;
32 import org.umu.cops.stack.COPSContext.RType;
33 import org.umu.cops.stack.COPSDecision.Command;
34 import org.umu.cops.stack.COPSDecision.DecisionFlag;
35 import org.umu.cops.stack.COPSObjHeader.CNum;
36 import org.umu.cops.stack.COPSObjHeader.CType;
37
38 import java.io.*;
39 import java.net.InetAddress;
40 import java.net.Socket;
41 import java.util.HashMap;
42 import java.util.HashSet;
43 import java.util.Map;
44 import java.util.Set;
45
46 /**
47  * Tests the PCMMService's ability to connect to a CMTS. Gate additions will not properly work as there is currently
48  * not any other means to receive acknowledgements. This functionality must be tested by the PCMMService's client
49  * PacketcableProvider.
50  */
51 public class PCMMServiceTest {
52
53     private final static String ccapId = "ccap-1";
54     private final static String gatePath = "testGatePath";
55
56     /**
57      * Denotes whether or not a real CMTS is being tested against.
58      * Ensure the checked-in value is always false else tests will most likely fail.
59      */
60     private final static boolean realCmts = false;
61
62
63     // The test objects/values to use that will be instantiated in @Before
64
65     /**
66      * The mock CMTS running on localhost with a dynamic port assigned.
67      */
68     private CMTS icmts;
69
70     /**
71      * The IP address object for the CMTS to test against
72      */
73     private Ipv4Address cmtsAddr;
74
75     /**
76      * The gate classifier's srcIp value, any valid IP should work.
77      */
78     private Ipv4Address srcAddr;
79
80     /**
81      * The gate classifier's dstIp value, any valid IP should work.
82      */
83     private Ipv4Address dstAddr;
84
85     /**
86      * Defines the CMTS to add to the PCMMService
87      */
88     private Ccaps ccap;
89
90     /**
91      * The class under test
92      */
93     private PCMMService service;
94
95     /**
96      * The cable modem IP address to which a gate should be set
97      */
98     private InetAddress cmAddrInet;
99     private InetAddress invalidCmAddrInet;
100
101     @Before
102     public void setup() throws IOException {
103         srcAddr = new Ipv4Address("10.10.10.0");
104         dstAddr = new Ipv4Address("10.32.99.99");
105
106         if (realCmts) {
107             cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
108             invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
109
110             // Use me when testing against a CMTS or emulator not running in the same JVM
111             cmtsAddr = new Ipv4Address("10.32.10.3");
112             ccap = makeCcapsObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId);
113         } else {
114             cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
115             invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
116
117             // Use me for automated testing and the CMTS emulator running in the same JVM
118             cmtsAddr = new Ipv4Address("127.0.0.1");
119
120             final Set<String> upGates = new HashSet<>();
121             upGates.add("extrm_up");
122             final Set<String> dnGates = new HashSet<>();
123             dnGates.add("extrm_dn");
124             final Map<Direction, Set<String>> gates = new HashMap<>();
125             gates.put(Direction.UPSTREAM, upGates);
126             gates.put(Direction.DOWNSTREAM, dnGates);
127
128             final Map<String, Boolean> cmStatus = new HashMap<>();
129             cmStatus.put(cmAddrInet.getHostAddress(), true);
130             cmStatus.put(invalidCmAddrInet.getHostAddress(), false);
131             icmts = new CMTS(gates, cmStatus);
132             icmts.startServer();
133
134             ccap = makeCcapsObj(icmts.getPort(), cmtsAddr, ccapId);
135         }
136
137         service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
138     }
139
140     @After
141     public void tearDown() {
142         if (icmts != null) icmts.stopServer();
143         service.disconect();
144     }
145
146     @Test
147     public void testAddCcap() {
148         connectToCmts(service);
149     }
150
151     @Test
152     public void testAddInvalidCcapBadPort() {
153         ccap = makeCcapsObj((icmts.getPort() + 1), cmtsAddr, ccapId);
154         service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
155         final String message = service.addCcap();
156         Assert.assertNotNull(message);
157         final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
158                 + ':' + (icmts.getPort() + 1) + " - ";
159         Assert.assertTrue(expectedMsg, message.startsWith(expectedMsg));
160     }
161
162     @Test
163     public void testAddValidUpGateTwice() throws Exception {
164         connectToCmts(service);
165         final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
166         addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
167                 expectedMsg1);
168
169         final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
170         addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
171                 expectedMsg2);
172
173         Assert.assertTrue(deleteGate(service, gatePath));
174     }
175
176     @Test
177     public void testAddTwoValidUpGates() throws Exception {
178         connectToCmts(service);
179
180         final String gatePath1 = "gatePath1";
181         final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath1 + " returned GateId";
182         addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath1,
183                 expectedMsg1);
184
185         final String gatePath2 = "gatePath2";
186         final String expectedMsg2 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath2 + " returned GateId";
187         addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath2,
188                 expectedMsg2);
189
190         Assert.assertTrue(deleteGate(service, gatePath1));
191         Assert.assertTrue(deleteGate(service, gatePath2));
192     }
193
194     @Test
195     public void testAddValidDownGateTwice() throws Exception {
196         connectToCmts(service);
197         final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
198         addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
199                 expectedMsg1);
200
201         final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
202         addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
203                 expectedMsg2);
204
205         Assert.assertTrue(deleteGate(service, gatePath));
206     }
207
208     @Test
209     public void testDeleteNonExistentGate() throws Exception {
210         connectToCmts(service);
211         Assert.assertFalse(deleteGate(service, gatePath));
212     }
213
214     @Test
215     public void testAddAndRemoveValidUpGate() throws Exception {
216         final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
217         addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
218                 expectedMsgStart);
219     }
220
221     @Test
222     public void testAddAndRemoveValidDownGate() throws Exception {
223         final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
224         addRemoveValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
225                 expectedMsgStart);
226     }
227
228     @Test
229     public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception {
230         // TODO - fix cmts emulator
231         final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
232                 + " returned error - Error Code: 13 Error Subcode : 0  Invalid SubscriberID";
233         addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet,
234                 gatePath, expectedMsgStart);
235     }
236
237     @Test
238     public void testAddInvalidScnUpGate() throws Exception {
239         final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
240                 + " returned error - Error Code: 11 Error Subcode : 0  Undefined Service Class Name";
241         addRemoveValidateGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet,
242                 gatePath, expectedMsgStart);
243     }
244
245     @Test
246     public void testAddInvalidScnDownGate() throws Exception {
247         final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
248                 + " returned error - Error Code: 11 Error Subcode : 0  Undefined Service Class Name";
249         addRemoveValidateGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet,
250                 gatePath, expectedMsgStart);
251     }
252
253     /**
254      * This tests the instantiation of a COPSDecisionMsg object that is responsible for setting a gate request,
255      * streams it over a mock Socket object and parses the bytes into a new COPSDecisionMsg object which should
256      * be equivalent
257      * @throws Exception - test will fail should any exception be thrown during execution
258      */
259     @Test
260     public void testGateRequestDecisionMsg() throws Exception {
261         final Socket socket = new MockSocket();
262
263         final ServiceFlowDirection direction = ServiceFlowDirection.Us;
264         final Gates gate = makeGateObj("extrm_up", cmtsAddr, direction, new Ipv4Address("127.0.0.1"));
265         final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"), direction);
266         final byte[] data = gateReq.getData();
267
268         final Set<COPSDecision> decisionSet = new HashSet<>();
269         decisionSet.add(new COPSDecision(CType.DEF, Command.INSTALL, DecisionFlag.REQERROR));
270         final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
271         decisionMap.put(new COPSContext(RType.CONFIG, (short) 0), decisionSet);
272
273         final COPSClientSI clientSD = new COPSClientSI(CNum.DEC, CType.CSI, new COPSData(data, 0, data.length));
274         final COPSDecisionMsg decisionMsg = new COPSDecisionMsg(IPCMMClient.CLIENT_TYPE, new COPSHandle(new COPSData("123")),
275                 decisionMap, null, clientSD);
276         decisionMsg.writeData(socket);
277
278         final COPSMsg msg = COPSMsgParser.parseMessage(socket);
279         Assert.assertNotNull(msg);
280         Assert.assertEquals(decisionMsg, msg);
281     }
282
283     /**
284      * Attempts to create a gate against a CMTS, validates the results then attempts to delete it.
285      * @param service - the service used to connect to a CMTS for issuing requests
286      * @param scnName - the service class name (aka. gate name)
287      * @param srcAddr - the address to the CMTS subnet?
288      * @param dstAddr - the destination address
289      * @param direction - the gate direction
290      * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
291      * @param gatePath - the path to the gate
292      * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
293      */
294     private void addRemoveValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
295                                        final Ipv4Address dstAddr, final ServiceFlowDirection direction,
296                                        final InetAddress cmAddrInet, final String gatePath,
297                                        final String expGateSetMsgStart) {
298         connectToCmts(service);
299         addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
300         deleteGate(service, gatePath);
301     }
302
303     private void connectToCmts(final PCMMService service) {
304         final String message = service.addCcap();
305         Assert.assertNotNull(message);
306         final String expectedMsg = "200 OK - CCAP " + ccapId + " connected @ "
307                 + ccap.getConnection().getIpAddress().getIpv4Address().getValue()
308                 + ":" + ccap.getConnection().getPort().getValue();
309         Assert.assertEquals(expectedMsg, message);
310         Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
311     }
312
313     /**
314      * Attempts to create a gate against a CMTS and validates the results.
315      * @param service - the service used to connect to a CMTS for issuing requests
316      * @param scnName - the service class name (aka. gate name)
317      * @param srcAddr - the address to the CMTS subnet?
318      * @param dstAddr - the destination address
319      * @param direction - the gate direction
320      * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
321      * @param gatePath - the path to the gate
322      * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
323      */
324     private void addAndValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
325                                     final Ipv4Address dstAddr, final ServiceFlowDirection direction,
326                                     final InetAddress cmAddrInet, final String gatePath,
327                                     final String expGateSetMsgStart) {
328         final Gates gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
329
330         final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
331         Assert.assertNotNull(gateSetMsg);
332         Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
333
334         // TODO - add validation to the PCMMGateReq contained within the map
335         Assert.assertNotNull(service.gateRequests.get(gatePath));
336     }
337
338     /**
339      * Attempts to delete a gate
340      * @param service - the service used to connect to a CMTS for issuing requests
341      * @param gatePath - the path to the gate
342      */
343     private boolean deleteGate(final PCMMService service, final String gatePath) {
344         final boolean out = service.sendGateDelete(gatePath);
345
346         // Wait up to 1 sec for response to be processed
347         final long start = System.currentTimeMillis();
348         while (1000 < System.currentTimeMillis() - start) {
349             if (service.gateRequests.size() == 0) break;
350         }
351         Assert.assertNull(service.gateRequests.get(gatePath));
352         return out;
353     }
354
355     /**
356      * Creates a mock Ccaps object that can be used for connecting to a CMTS
357      * @param inPort - the CMTS port number
358      * @param ipAddr - the CMTS IPv4 address string
359      * @param ccapId - the ID of the CCAP
360      * @return - the mock Ccaps object
361      */
362     private Ccaps makeCcapsObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
363         final Ccaps ccap = Mockito.mock(Ccaps.class);
364         final Connection conn = Mockito.mock(Connection.class);
365         Mockito.when(ccap.getConnection()).thenReturn(conn);
366         final PortNumber port = Mockito.mock(PortNumber.class);
367         Mockito.when(conn.getPort()).thenReturn(port);
368         Mockito.when(port.getValue()).thenReturn(inPort);
369
370         final IpAddress addr = Mockito.mock(IpAddress.class);
371         Mockito.when(conn.getIpAddress()).thenReturn(addr);
372         Mockito.when(addr.getIpv4Address()).thenReturn(ipAddr);
373
374         Mockito.when(ccap.getCcapId()).thenReturn(ccapId);
375         final AmId amid = Mockito.mock(AmId.class);
376         Mockito.when(ccap.getAmId()).thenReturn(amid);
377         Mockito.when(amid.getAmTag()).thenReturn(0xcada);
378         Mockito.when(amid.getAmType()).thenReturn(1);
379
380         return ccap;
381     }
382
383     /**
384      * Creates a mock Gates object
385      * @param scnValue - the service class name defined on the CMTS
386      * @param dstAddr - the CM address this gate should be set against
387      * @return - the gate request
388      */
389     private Gates makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
390                               final Ipv4Address dstAddr) {
391         final Gates gate = Mockito.mock(Gates.class);
392         final GateSpec gateSpec = Mockito.mock(GateSpec.class);
393         Mockito.when(gate.getGateSpec()).thenReturn(gateSpec);
394         Mockito.when(gateSpec.getDirection()).thenReturn(direction);
395         // TODO - make sure to write a test when this value is not null
396         Mockito.when(gateSpec.getDscpTosOverwrite()).thenReturn(null);
397         final TrafficProfile trafficProfile = Mockito.mock(TrafficProfile.class);
398         final ServiceClassName scn = Mockito.mock(ServiceClassName.class);
399         Mockito.when(scn.getValue()).thenReturn(scnValue);
400         Mockito.when(trafficProfile.getServiceClassName()).thenReturn(scn);
401         Mockito.when(gate.getTrafficProfile()).thenReturn(trafficProfile);
402
403         // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
404         final Classifier classifier = Mockito.mock(Classifier.class);
405
406         // This is the address of the CM
407         Mockito.when(classifier.getDstIp()).thenReturn(dstAddr);
408
409         final PortNumber dstPort = new PortNumber(4321);
410         Mockito.when(classifier.getDstPort()).thenReturn(dstPort);
411         final TpProtocol protocol = new TpProtocol(0);
412         Mockito.when(classifier.getProtocol()).thenReturn(protocol);
413         Mockito.when(classifier.getSrcIp()).thenReturn(srcAddr);
414         final PortNumber srcPort = new PortNumber(1234);
415         Mockito.when(classifier.getSrcPort()).thenReturn(srcPort);
416         final TosByte tosByte = new TosByte((short)160);
417         Mockito.when(classifier.getTosByte()).thenReturn(tosByte);
418         final TosByte tosMask = new TosByte((short)224);
419         Mockito.when(classifier.getTosMask()).thenReturn(tosMask);
420
421         // TODO - enhance to test support of the other classifier types
422         Mockito.when(gate.getClassifier()).thenReturn(classifier);
423         Mockito.when(gate.getExtClassifier()).thenReturn(null);
424         Mockito.when(gate.getIpv6Classifier()).thenReturn(null);
425         return gate;
426     }
427
428     private IPCMMGate makeGateRequest(final Ccaps ccap, final Gates gateReq, final InetAddress addrSubId,
429                                      final ServiceFlowDirection direction) {
430         final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
431         gateBuilder.build(ccap.getAmId());
432         gateBuilder.build(addrSubId);
433         // force gateSpec.Direction to align with SCN direction
434         final ServiceClassName scn = gateReq.getTrafficProfile().getServiceClassName();
435         if (scn != null) {
436             gateBuilder.build(gateReq.getGateSpec(), direction);
437         } else {
438             // not an SCN gate
439             gateBuilder.build(gateReq.getGateSpec(), null);
440         }
441         gateBuilder.build(gateReq.getTrafficProfile());
442
443         // pick a classifier type (only one for now)
444         if (gateReq.getClassifier() != null) {
445             gateBuilder.build(gateReq.getClassifier());
446         } else if (gateReq.getExtClassifier() != null) {
447             gateBuilder.build(gateReq.getExtClassifier());
448         } else if (gateReq.getIpv6Classifier() != null) {
449             gateBuilder.build(gateReq.getIpv6Classifier());
450         }
451         // assemble the final gate request
452         return gateBuilder.getGateReq();
453     }
454
455     private class MockSocket extends Socket {
456
457         private ByteArrayOutputStream os = new ByteArrayOutputStream();
458         private ByteArrayInputStream is;
459
460         @Override
461         public OutputStream getOutputStream() {
462             return os;
463         }
464
465         @Override
466         public InputStream getInputStream() {
467             if (is == null) is = new ByteArrayInputStream(os.toByteArray());
468             return is;
469         }
470     }
471
472 }