2 * (c) 2015 Cable Television Laboratories, Inc. All rights reserved.
5 package org.opendaylight.controller.packetcable.provider;
7 import static junit.framework.TestCase.assertEquals;
8 import static org.mockito.Mockito.mock;
9 import static org.mockito.Mockito.when;
11 import java.io.ByteArrayInputStream;
12 import java.io.ByteArrayOutputStream;
13 import java.io.IOException;
14 import java.io.InputStream;
15 import java.io.OutputStream;
16 import java.net.InetAddress;
17 import java.net.Socket;
18 import java.util.Collections;
19 import java.util.HashMap;
20 import java.util.HashSet;
21 import java.util.List;
24 import org.junit.After;
25 import org.junit.Assert;
26 import org.junit.Before;
27 import org.junit.Test;
28 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
29 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
30 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
31 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceClassName;
32 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceFlowDirection;
33 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.TosByte;
34 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.TpProtocol;
35 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.attributes.AmId;
36 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.attributes.Connection;
37 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccaps.Ccap;
38 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.Classifiers;
39 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.ClassifierContainer;
40 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.classifier.container.classifier.choice.QosClassifierChoice;
41 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.classifier.Classifier;
42 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gate.spec.GateSpec;
43 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
44 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.traffic.profile.TrafficProfile;
45 import org.pcmm.PCMMPdpAgent;
46 import org.pcmm.gates.IGateSpec.Direction;
47 import org.pcmm.gates.IPCMMGate;
48 import org.pcmm.rcd.IPCMMClient;
49 import org.pcmm.rcd.impl.CMTS;
50 import org.umu.cops.stack.COPSClientSI;
51 import org.umu.cops.stack.COPSContext;
52 import org.umu.cops.stack.COPSContext.RType;
53 import org.umu.cops.stack.COPSData;
54 import org.umu.cops.stack.COPSDecision;
55 import org.umu.cops.stack.COPSDecision.Command;
56 import org.umu.cops.stack.COPSDecision.DecisionFlag;
57 import org.umu.cops.stack.COPSDecisionMsg;
58 import org.umu.cops.stack.COPSHandle;
59 import org.umu.cops.stack.COPSMsg;
60 import org.umu.cops.stack.COPSMsgParser;
61 import org.umu.cops.stack.COPSObjHeader.CNum;
62 import org.umu.cops.stack.COPSObjHeader.CType;
65 * Tests the PCMMService's ability to connect to a CMTS. Gate additions will not properly work as there is currently
66 * not any other means to receive acknowledgements. This functionality must be tested by the PCMMService's client
67 * PacketcableProvider.
69 public class PCMMServiceTest {
71 private static final String ccapId = "ccap-1";
72 private static final String gatePath = "testGatePath";
75 * Denotes whether or not a real CMTS is being tested against.
76 * Ensure the checked-in value is always false else tests will most likely fail.
78 private static final boolean realCmts = false;
81 // The test objects/values to use that will be instantiated in @Before
84 * The mock CMTS running on localhost with a dynamic port assigned.
89 * The IP address object for the CMTS to test against
91 private Ipv4Address cmtsAddr;
94 * The gate classifier's srcIp value, any valid IP should work.
96 private Ipv4Address srcAddr;
99 * The gate classifier's dstIp value, any valid IP should work.
101 private Ipv4Address dstAddr;
104 * Defines the CMTS to add to the PCMMService
109 * The class under test
111 private PCMMService service;
114 * The cable modem IP address to which a gate should be set
116 private InetAddress cmAddrInet;
117 private InetAddress invalidCmAddrInet;
120 public void setup() throws IOException {
121 srcAddr = new Ipv4Address("10.10.10.0");
122 dstAddr = new Ipv4Address("10.32.99.99");
125 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)172});
126 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
128 // Use me when testing against a CMTS or emulator not running in the same JVM
129 cmtsAddr = new Ipv4Address("10.32.10.3");
130 ccap = makeCcapObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId);
132 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
133 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
135 // Use me for automated testing and the CMTS emulator running in the same JVM
136 cmtsAddr = new Ipv4Address("127.0.0.1");
138 final Set<String> upGate = new HashSet<>();
139 upGate.add("extrm_up");
140 final Set<String> dnGate = new HashSet<>();
141 dnGate.add("extrm_dn");
142 final Map<Direction, Set<String>> gates = new HashMap<>();
143 gates.put(Direction.UPSTREAM, upGate);
144 gates.put(Direction.DOWNSTREAM, dnGate);
146 final Map<String, Boolean> cmStatus = new HashMap<>();
147 cmStatus.put(cmAddrInet.getHostAddress(), true);
148 cmStatus.put(invalidCmAddrInet.getHostAddress(), false);
149 icmts = new CMTS(gates, cmStatus);
152 ccap = makeCcapObj(icmts.getPort(), cmtsAddr, ccapId);
155 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
159 public void tearDown() {
160 if (icmts != null) icmts.stopServer();
165 public void testAddCcap() {
166 connectToCmts(service);
170 public void testAddInvalidCcapBadPort() {
172 if (icmts != null) port = icmts.getPort() + 1;
173 else port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT + 1;
174 ccap = makeCcapObj(port, cmtsAddr, ccapId);
175 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
176 final String message = service.addCcap();
177 Assert.assertNotNull(message);
178 final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
179 + ':' + port + " - ";
180 Assert.assertTrue(expectedMsg, message.startsWith(expectedMsg));
184 public void testAddValidUpGateTwice() throws Exception {
185 connectToCmts(service);
186 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
187 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
190 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
191 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
194 Assert.assertTrue(deleteGate(service, gatePath));
198 public void testAddTwoValidUpGates() throws Exception {
199 connectToCmts(service);
201 final String gatePath1 = "gatePath1";
202 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath1 + " returned GateId";
203 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath1,
206 final String gatePath2 = "gatePath2";
207 final String expectedMsg2 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath2 + " returned GateId";
208 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath2,
211 Assert.assertTrue(deleteGate(service, gatePath1));
212 Assert.assertTrue(deleteGate(service, gatePath2));
216 public void testAddValidDownGateTwice() throws Exception {
217 connectToCmts(service);
218 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
219 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
222 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
223 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
226 Assert.assertTrue(deleteGate(service, gatePath));
230 public void testDeleteNonExistentGate() throws Exception {
231 connectToCmts(service);
232 Assert.assertFalse(deleteGate(service, gatePath));
236 public void testAddAndRemoveValidUpGate() throws Exception {
237 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
238 addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
243 public void testAddAndRemoveValidDownGate() throws Exception {
244 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
245 addRemoveValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
250 public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception {
251 // TODO - fix cmts emulator
252 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
253 + " returned error - Error Code: 13 Error Subcode : 0 Invalid SubscriberID";
254 addInvalidGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet, gatePath,
259 public void testAddInvalidScnUpGate() 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 addInvalidGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
267 public void testAddInvalidScnDownGate() throws Exception {
268 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
269 + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name";
270 addInvalidGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
275 * This tests the instantiation of a COPSDecisionMsg object that is responsible for setting a gate request,
276 * streams it over a mock Socket object and parses the bytes into a new COPSDecisionMsg object which should
278 * @throws Exception - test will fail should any exception be thrown during execution
281 public void testGateRequestDecisionMsg() throws Exception {
282 final Socket socket = new MockSocket();
284 final ServiceFlowDirection direction = ServiceFlowDirection.Us;
285 final Gate gate = makeGateObj("extrm_up", cmtsAddr, direction, new Ipv4Address("127.0.0.1"));
286 final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"), direction);
287 final byte[] data = gateReq.getData();
289 final Set<COPSDecision> decisionSet = new HashSet<>();
290 decisionSet.add(new COPSDecision(CType.DEF, Command.INSTALL, DecisionFlag.REQERROR));
291 final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
292 decisionMap.put(new COPSContext(RType.CONFIG, (short) 0), decisionSet);
294 final COPSClientSI clientSD = new COPSClientSI(CNum.DEC, CType.CSI, new COPSData(data, 0, data.length));
295 final COPSDecisionMsg decisionMsg = new COPSDecisionMsg(IPCMMClient.CLIENT_TYPE, new COPSHandle(new COPSData("123")),
296 decisionMap, null, clientSD);
297 decisionMsg.writeData(socket);
299 final COPSMsg msg = COPSMsgParser.parseMessage(socket);
300 Assert.assertNotNull(msg);
301 Assert.assertEquals(decisionMsg, msg);
305 * Attempts to create a gate against a CMTS, validates the results then attempts to delete it.
306 * @param service - the service used to connect to a CMTS for issuing requests
307 * @param scnName - the service class name (aka. gate name)
308 * @param srcAddr - the address to the CMTS subnet?
309 * @param dstAddr - the destination address
310 * @param direction - the gate direction
311 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
312 * @param gatePath - the path to the gate
313 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
315 private void addRemoveValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
316 final Ipv4Address dstAddr, final ServiceFlowDirection direction,
317 final InetAddress cmAddrInet, final String gatePath,
318 final String expGateSetMsgStart) {
319 connectToCmts(service);
320 addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
321 deleteGate(service, gatePath);
324 private void addInvalidGate(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 connectToCmts(service);
329 final int numRequestsBefore = service.gateRequests.size();
330 addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
331 assertEquals(numRequestsBefore, service.gateRequests.size());
334 private void connectToCmts(final PCMMService service) {
335 final String message = service.addCcap();
336 Assert.assertNotNull(message);
337 final String expectedMsg = "200 OK - CCAP " + ccapId + " connected @ "
338 + ccap.getConnection().getIpAddress().getIpv4Address().getValue()
339 + ":" + ccap.getConnection().getPort().getValue();
340 Assert.assertEquals(expectedMsg, message);
341 Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
345 * Attempts to create a gate against a CMTS and validates the results.
346 * @param service - the service used to connect to a CMTS for issuing requests
347 * @param scnName - the service class name (aka. gate name)
348 * @param srcAddr - the address to the CMTS subnet?
349 * @param dstAddr - the destination address
350 * @param direction - the gate direction
351 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
352 * @param gatePath - the path to the gate
353 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
355 private void addAndValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
356 final Ipv4Address dstAddr, final ServiceFlowDirection direction,
357 final InetAddress cmAddrInet, final String gatePath,
358 final String expGateSetMsgStart) {
359 final Gate gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
361 // final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
362 // Assert.assertNotNull(gateSetMsg);
363 // Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
365 // TODO update this method for the new GateSetStatus object
366 PCMMService.GateSetStatus status = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
367 Assert.assertNotNull(status);
368 Assert.assertTrue(status.getMessage().startsWith(expGateSetMsgStart));
370 // TODO - add validation to the PCMMGateReq contained within the map
371 if (status.didSucceed()) {
372 Assert.assertTrue((service.gateRequests.containsKey(gatePath)));
373 Assert.assertNotNull(service.gateRequests.get(gatePath));
378 * Attempts to delete a gate
379 * @param service - the service used to connect to a CMTS for issuing requests
380 * @param gatePath - the path to the gate
382 private boolean deleteGate(final PCMMService service, final String gatePath) {
383 final boolean out = service.sendGateDelete(gatePath);
385 // Wait up to 1 sec for response to be processed
386 final long start = System.currentTimeMillis();
387 while (1000 < System.currentTimeMillis() - start) {
388 if (service.gateRequests.size() == 0) break;
390 Assert.assertNull(service.gateRequests.get(gatePath));
395 * Creates a mock Ccap object that can be used for connecting to a CMTS
396 * @param inPort - the CMTS port number
397 * @param ipAddr - the CMTS IPv4 address string
398 * @param ccapId - the ID of the CCAP
399 * @return - the mock Ccap object
401 private Ccap makeCcapObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
402 final Ccap ccap = mock(Ccap.class);
403 final Connection conn = mock(Connection.class);
404 when(ccap.getConnection()).thenReturn(conn);
405 final PortNumber port = mock(PortNumber.class);
406 when(conn.getPort()).thenReturn(port);
407 when(port.getValue()).thenReturn(inPort);
409 final IpAddress addr = mock(IpAddress.class);
410 when(conn.getIpAddress()).thenReturn(addr);
411 when(addr.getIpv4Address()).thenReturn(ipAddr);
413 when(ccap.getCcapId()).thenReturn(ccapId);
414 final AmId amid = mock(AmId.class);
415 when(ccap.getAmId()).thenReturn(amid);
416 when(amid.getAmTag()).thenReturn(0xcada);
417 when(amid.getAmType()).thenReturn(1);
423 * Creates a mock Gate object
424 * @param scnValue - the service class name defined on the CMTS
425 * @param dstAddr - the CM address this gate should be set against
426 * @return - the gate request
428 private Gate makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
429 final Ipv4Address dstAddr) {
430 final Gate gate = mock(Gate.class);
431 final GateSpec gateSpec = mock(GateSpec.class);
432 when(gate.getGateSpec()).thenReturn(gateSpec);
433 when(gateSpec.getDirection()).thenReturn(direction);
434 // TODO - make sure to write a test when this value is not null
435 when(gateSpec.getDscpTosOverwrite()).thenReturn(null);
436 final TrafficProfile trafficProfile = mock(TrafficProfile.class);
437 final ServiceClassName scn = mock(ServiceClassName.class);
438 when(scn.getValue()).thenReturn(scnValue);
439 when(trafficProfile.getServiceClassName()).thenReturn(scn);
440 when(gate.getTrafficProfile()).thenReturn(trafficProfile);
442 // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
443 final Classifier classifier = mock(Classifier.class);
445 // This is the address of the CM
446 when(classifier.getDstIp()).thenReturn(dstAddr);
448 final PortNumber dstPort = new PortNumber(4321);
449 when(classifier.getDstPort()).thenReturn(dstPort);
450 final TpProtocol protocol = new TpProtocol(0);
451 when(classifier.getProtocol()).thenReturn(protocol);
452 when(classifier.getSrcIp()).thenReturn(srcAddr);
453 final PortNumber srcPort = new PortNumber(1234);
454 when(classifier.getSrcPort()).thenReturn(srcPort);
456 // TODO - Can this value be any other value than 0 or 1 (See TosByte enumeration)
457 final TosByte tosByte = new TosByte((short)0);
458 when(classifier.getTosByte()).thenReturn(tosByte);
459 final TosByte tosMask = new TosByte((short)224);
460 when(classifier.getTosMask()).thenReturn(tosMask);
462 final QosClassifierChoice classifierChoice = mock(QosClassifierChoice.class);
463 when(classifierChoice.getClassifier()).thenReturn(classifier);
465 ClassifierContainer classifierContainer = mock(ClassifierContainer.class);
466 when(classifierContainer.getClassifierChoice()).thenReturn(classifierChoice);
468 final List<ClassifierContainer> containerList = Collections.singletonList(classifierContainer);
470 Classifiers classifiers = mock(Classifiers.class);
471 when(classifiers.getClassifierContainer()).thenReturn(containerList);
473 when(gate.getClassifiers()).thenReturn(classifiers);
478 private IPCMMGate makeGateRequest(final Ccap ccap, final Gate gateReq, final InetAddress addrSubId,
479 final ServiceFlowDirection direction) {
480 final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
481 gateBuilder.setAmId(ccap.getAmId());
482 gateBuilder.setSubscriberId(addrSubId);
483 // force gateSpec.Direction to align with SCN direction
484 final ServiceClassName scn = gateReq.getTrafficProfile().getServiceClassName();
486 gateBuilder.setGateSpec(gateReq.getGateSpec(), direction);
489 gateBuilder.setGateSpec(gateReq.getGateSpec(), null);
491 gateBuilder.setTrafficProfile(gateReq.getTrafficProfile());
493 gateBuilder.setClassifiers(gateReq.getClassifiers().getClassifierContainer());
495 // assemble the final gate request
496 return gateBuilder.build();
499 private class MockSocket extends Socket {
501 private ByteArrayOutputStream os = new ByteArrayOutputStream();
502 private ByteArrayInputStream is;
505 public OutputStream getOutputStream() {
510 public InputStream getInputStream() {
511 if (is == null) is = new ByteArrayInputStream(os.toByteArray());