55b9e6e9ad94cb208247d94e5758ed17474290e3
[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)172});
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         final int port;
154         if (icmts != null) port = icmts.getPort() + 1;
155         else port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT + 1;
156         ccap = makeCcapsObj(port, cmtsAddr, ccapId);
157         service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
158         final String message = service.addCcap();
159         Assert.assertNotNull(message);
160         final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
161                 + ':' + port + " - ";
162         Assert.assertTrue(expectedMsg, message.startsWith(expectedMsg));
163     }
164
165     @Test
166     public void testAddValidUpGateTwice() throws Exception {
167         connectToCmts(service);
168         final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
169         addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
170                 expectedMsg1);
171
172         final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
173         addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
174                 expectedMsg2);
175
176         Assert.assertTrue(deleteGate(service, gatePath));
177     }
178
179     @Test
180     public void testAddTwoValidUpGates() throws Exception {
181         connectToCmts(service);
182
183         final String gatePath1 = "gatePath1";
184         final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath1 + " returned GateId";
185         addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath1,
186                 expectedMsg1);
187
188         final String gatePath2 = "gatePath2";
189         final String expectedMsg2 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath2 + " returned GateId";
190         addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath2,
191                 expectedMsg2);
192
193         Assert.assertTrue(deleteGate(service, gatePath1));
194         Assert.assertTrue(deleteGate(service, gatePath2));
195     }
196
197     @Test
198     public void testAddValidDownGateTwice() throws Exception {
199         connectToCmts(service);
200         final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
201         addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
202                 expectedMsg1);
203
204         final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
205         addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
206                 expectedMsg2);
207
208         Assert.assertTrue(deleteGate(service, gatePath));
209     }
210
211     @Test
212     public void testDeleteNonExistentGate() throws Exception {
213         connectToCmts(service);
214         Assert.assertFalse(deleteGate(service, gatePath));
215     }
216
217     @Test
218     public void testAddAndRemoveValidUpGate() throws Exception {
219         final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
220         addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
221                 expectedMsgStart);
222     }
223
224     @Test
225     public void testAddAndRemoveValidDownGate() throws Exception {
226         final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
227         addRemoveValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
228                 expectedMsgStart);
229     }
230
231     @Test
232     public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception {
233         // TODO - fix cmts emulator
234         final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
235                 + " returned error - Error Code: 13 Error Subcode : 0  Invalid SubscriberID";
236         addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet,
237                 gatePath, expectedMsgStart);
238     }
239
240     @Test
241     public void testAddInvalidScnUpGate() throws Exception {
242         final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
243                 + " returned error - Error Code: 11 Error Subcode : 0  Undefined Service Class Name";
244         addRemoveValidateGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet,
245                 gatePath, expectedMsgStart);
246     }
247
248     @Test
249     public void testAddInvalidScnDownGate() throws Exception {
250         final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
251                 + " returned error - Error Code: 11 Error Subcode : 0  Undefined Service Class Name";
252         addRemoveValidateGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet,
253                 gatePath, expectedMsgStart);
254     }
255
256     /**
257      * This tests the instantiation of a COPSDecisionMsg object that is responsible for setting a gate request,
258      * streams it over a mock Socket object and parses the bytes into a new COPSDecisionMsg object which should
259      * be equivalent
260      * @throws Exception - test will fail should any exception be thrown during execution
261      */
262     @Test
263     public void testGateRequestDecisionMsg() throws Exception {
264         final Socket socket = new MockSocket();
265
266         final ServiceFlowDirection direction = ServiceFlowDirection.Us;
267         final Gates gate = makeGateObj("extrm_up", cmtsAddr, direction, new Ipv4Address("127.0.0.1"));
268         final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"), direction);
269         final byte[] data = gateReq.getData();
270
271         final Set<COPSDecision> decisionSet = new HashSet<>();
272         decisionSet.add(new COPSDecision(CType.DEF, Command.INSTALL, DecisionFlag.REQERROR));
273         final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
274         decisionMap.put(new COPSContext(RType.CONFIG, (short) 0), decisionSet);
275
276         final COPSClientSI clientSD = new COPSClientSI(CNum.DEC, CType.CSI, new COPSData(data, 0, data.length));
277         final COPSDecisionMsg decisionMsg = new COPSDecisionMsg(IPCMMClient.CLIENT_TYPE, new COPSHandle(new COPSData("123")),
278                 decisionMap, null, clientSD);
279         decisionMsg.writeData(socket);
280
281         final COPSMsg msg = COPSMsgParser.parseMessage(socket);
282         Assert.assertNotNull(msg);
283         Assert.assertEquals(decisionMsg, msg);
284     }
285
286     /**
287      * Attempts to create a gate against a CMTS, validates the results then attempts to delete it.
288      * @param service - the service used to connect to a CMTS for issuing requests
289      * @param scnName - the service class name (aka. gate name)
290      * @param srcAddr - the address to the CMTS subnet?
291      * @param dstAddr - the destination address
292      * @param direction - the gate direction
293      * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
294      * @param gatePath - the path to the gate
295      * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
296      */
297     private void addRemoveValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
298                                        final Ipv4Address dstAddr, final ServiceFlowDirection direction,
299                                        final InetAddress cmAddrInet, final String gatePath,
300                                        final String expGateSetMsgStart) {
301         connectToCmts(service);
302         addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
303         deleteGate(service, gatePath);
304     }
305
306     private void connectToCmts(final PCMMService service) {
307         final String message = service.addCcap();
308         Assert.assertNotNull(message);
309         final String expectedMsg = "200 OK - CCAP " + ccapId + " connected @ "
310                 + ccap.getConnection().getIpAddress().getIpv4Address().getValue()
311                 + ":" + ccap.getConnection().getPort().getValue();
312         Assert.assertEquals(expectedMsg, message);
313         Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
314     }
315
316     /**
317      * Attempts to create a gate against a CMTS and validates the results.
318      * @param service - the service used to connect to a CMTS for issuing requests
319      * @param scnName - the service class name (aka. gate name)
320      * @param srcAddr - the address to the CMTS subnet?
321      * @param dstAddr - the destination address
322      * @param direction - the gate direction
323      * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
324      * @param gatePath - the path to the gate
325      * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
326      */
327     private void addAndValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
328                                     final Ipv4Address dstAddr, final ServiceFlowDirection direction,
329                                     final InetAddress cmAddrInet, final String gatePath,
330                                     final String expGateSetMsgStart) {
331         final Gates gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
332
333         final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
334         Assert.assertNotNull(gateSetMsg);
335         Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
336
337         // TODO - add validation to the PCMMGateReq contained within the map
338         Assert.assertNotNull(service.gateRequests.get(gatePath));
339     }
340
341     /**
342      * Attempts to delete a gate
343      * @param service - the service used to connect to a CMTS for issuing requests
344      * @param gatePath - the path to the gate
345      */
346     private boolean deleteGate(final PCMMService service, final String gatePath) {
347         final boolean out = service.sendGateDelete(gatePath);
348
349         // Wait up to 1 sec for response to be processed
350         final long start = System.currentTimeMillis();
351         while (1000 < System.currentTimeMillis() - start) {
352             if (service.gateRequests.size() == 0) break;
353         }
354         Assert.assertNull(service.gateRequests.get(gatePath));
355         return out;
356     }
357
358     /**
359      * Creates a mock Ccaps object that can be used for connecting to a CMTS
360      * @param inPort - the CMTS port number
361      * @param ipAddr - the CMTS IPv4 address string
362      * @param ccapId - the ID of the CCAP
363      * @return - the mock Ccaps object
364      */
365     private Ccaps makeCcapsObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
366         final Ccaps ccap = Mockito.mock(Ccaps.class);
367         final Connection conn = Mockito.mock(Connection.class);
368         Mockito.when(ccap.getConnection()).thenReturn(conn);
369         final PortNumber port = Mockito.mock(PortNumber.class);
370         Mockito.when(conn.getPort()).thenReturn(port);
371         Mockito.when(port.getValue()).thenReturn(inPort);
372
373         final IpAddress addr = Mockito.mock(IpAddress.class);
374         Mockito.when(conn.getIpAddress()).thenReturn(addr);
375         Mockito.when(addr.getIpv4Address()).thenReturn(ipAddr);
376
377         Mockito.when(ccap.getCcapId()).thenReturn(ccapId);
378         final AmId amid = Mockito.mock(AmId.class);
379         Mockito.when(ccap.getAmId()).thenReturn(amid);
380         Mockito.when(amid.getAmTag()).thenReturn(0xcada);
381         Mockito.when(amid.getAmType()).thenReturn(1);
382
383         return ccap;
384     }
385
386     /**
387      * Creates a mock Gates object
388      * @param scnValue - the service class name defined on the CMTS
389      * @param dstAddr - the CM address this gate should be set against
390      * @return - the gate request
391      */
392     private Gates makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
393                               final Ipv4Address dstAddr) {
394         final Gates gate = Mockito.mock(Gates.class);
395         final GateSpec gateSpec = Mockito.mock(GateSpec.class);
396         Mockito.when(gate.getGateSpec()).thenReturn(gateSpec);
397         Mockito.when(gateSpec.getDirection()).thenReturn(direction);
398         // TODO - make sure to write a test when this value is not null
399         Mockito.when(gateSpec.getDscpTosOverwrite()).thenReturn(null);
400         final TrafficProfile trafficProfile = Mockito.mock(TrafficProfile.class);
401         final ServiceClassName scn = Mockito.mock(ServiceClassName.class);
402         Mockito.when(scn.getValue()).thenReturn(scnValue);
403         Mockito.when(trafficProfile.getServiceClassName()).thenReturn(scn);
404         Mockito.when(gate.getTrafficProfile()).thenReturn(trafficProfile);
405
406         // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
407         final Classifier classifier = Mockito.mock(Classifier.class);
408
409         // This is the address of the CM
410         Mockito.when(classifier.getDstIp()).thenReturn(dstAddr);
411
412         final PortNumber dstPort = new PortNumber(4321);
413         Mockito.when(classifier.getDstPort()).thenReturn(dstPort);
414         final TpProtocol protocol = new TpProtocol(0);
415         Mockito.when(classifier.getProtocol()).thenReturn(protocol);
416         Mockito.when(classifier.getSrcIp()).thenReturn(srcAddr);
417         final PortNumber srcPort = new PortNumber(1234);
418         Mockito.when(classifier.getSrcPort()).thenReturn(srcPort);
419
420         // TODO - Can this value be any other value than 0 or 1 (See TosByte enumeration)
421         final TosByte tosByte = new TosByte((short)0);
422         Mockito.when(classifier.getTosByte()).thenReturn(tosByte);
423         final TosByte tosMask = new TosByte((short)224);
424         Mockito.when(classifier.getTosMask()).thenReturn(tosMask);
425
426         // TODO - enhance to test support of the other classifier types
427         Mockito.when(gate.getClassifier()).thenReturn(classifier);
428         Mockito.when(gate.getExtClassifier()).thenReturn(null);
429         Mockito.when(gate.getIpv6Classifier()).thenReturn(null);
430         return gate;
431     }
432
433     private IPCMMGate makeGateRequest(final Ccaps ccap, final Gates gateReq, final InetAddress addrSubId,
434                                      final ServiceFlowDirection direction) {
435         final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
436         gateBuilder.build(ccap.getAmId());
437         gateBuilder.build(addrSubId);
438         // force gateSpec.Direction to align with SCN direction
439         final ServiceClassName scn = gateReq.getTrafficProfile().getServiceClassName();
440         if (scn != null) {
441             gateBuilder.build(gateReq.getGateSpec(), direction);
442         } else {
443             // not an SCN gate
444             gateBuilder.build(gateReq.getGateSpec(), null);
445         }
446         gateBuilder.build(gateReq.getTrafficProfile());
447
448         // pick a classifier type (only one for now)
449         if (gateReq.getClassifier() != null) {
450             gateBuilder.build(gateReq.getClassifier());
451         } else if (gateReq.getExtClassifier() != null) {
452             gateBuilder.build(gateReq.getExtClassifier());
453         } else if (gateReq.getIpv6Classifier() != null) {
454             gateBuilder.build(gateReq.getIpv6Classifier());
455         }
456         // assemble the final gate request
457         return gateBuilder.getGateReq();
458     }
459
460     private class MockSocket extends Socket {
461
462         private ByteArrayOutputStream os = new ByteArrayOutputStream();
463         private ByteArrayInputStream is;
464
465         @Override
466         public OutputStream getOutputStream() {
467             return os;
468         }
469
470         @Override
471         public InputStream getInputStream() {
472             if (is == null) is = new ByteArrayInputStream(os.toByteArray());
473             return is;
474         }
475     }
476
477 }