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