2 * (c) 2015 Cable Television Laboratories, Inc. All rights reserved.
5 package org.opendaylight.controller.packetcable.provider;
7 import static junit.framework.TestCase.assertEquals;
9 import static org.hamcrest.CoreMatchers.startsWith;
10 import static org.hamcrest.MatcherAssert.assertThat;
11 import static org.mockito.Mockito.mock;
12 import static org.mockito.Mockito.when;
14 import java.io.ByteArrayInputStream;
15 import java.io.ByteArrayOutputStream;
16 import java.io.IOException;
17 import java.io.InputStream;
18 import java.io.OutputStream;
19 import java.net.InetAddress;
20 import java.net.Socket;
21 import java.util.Collections;
22 import java.util.HashMap;
23 import java.util.HashSet;
24 import java.util.List;
27 import org.junit.After;
28 import org.junit.Assert;
29 import org.junit.Before;
30 import org.junit.Test;
31 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.IpAddress;
32 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.Ipv4Address;
33 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
34 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceClassName;
35 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceFlowDirection;
36 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.TosByte;
37 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.TpProtocol;
38 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.attributes.AmId;
39 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.attributes.Connection;
40 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccaps.Ccap;
41 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.Classifiers;
42 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.ClassifierContainer;
43 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.classifier.attributes.classifiers.classifier.container.classifier.choice.QosClassifierChoice;
44 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.classifier.Classifier;
45 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gate.spec.GateSpec;
46 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
47 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.traffic.profile.TrafficProfile;
48 import org.pcmm.PCMMPdpAgent;
49 import org.pcmm.gates.IPCMMGate;
50 import org.pcmm.rcd.IPCMMClient;
51 import org.pcmm.rcd.impl.CMTS;
52 import org.pcmm.rcd.impl.CMTSConfig;
53 import org.umu.cops.stack.COPSClientSI;
54 import org.umu.cops.stack.COPSContext;
55 import org.umu.cops.stack.COPSContext.RType;
56 import org.umu.cops.stack.COPSData;
57 import org.umu.cops.stack.COPSDecision;
58 import org.umu.cops.stack.COPSDecision.Command;
59 import org.umu.cops.stack.COPSDecision.DecisionFlag;
60 import org.umu.cops.stack.COPSDecisionMsg;
61 import org.umu.cops.stack.COPSHandle;
62 import org.umu.cops.stack.COPSMsg;
63 import org.umu.cops.stack.COPSMsgParser;
64 import org.umu.cops.stack.COPSObjHeader.CNum;
65 import org.umu.cops.stack.COPSObjHeader.CType;
68 * Tests the PCMMService's ability to connect to a CMTS. Gate additions will not properly work as there is currently
69 * not any other means to receive acknowledgements. This functionality must be tested by the PCMMService's client
70 * PacketcableProvider.
72 public class PCMMServiceTest {
74 private static final String ccapId = "ccap-1";
75 private static final String gatePath = "testGatePath";
78 * Denotes whether or not a real CMTS is being tested against.
79 * Ensure the checked-in value is always false else tests will most likely fail.
81 private static final boolean realCmts = false;
84 // The test objects/values to use that will be instantiated in @Before
87 * The mock CMTS running on localhost with a dynamic port assigned.
92 * The IP address object for the CMTS to test against
94 private Ipv4Address cmtsAddr;
97 * The gate classifier's srcIp value, any valid IP should work.
99 private Ipv4Address srcAddr;
102 * The gate classifier's dstIp value, any valid IP should work.
104 private Ipv4Address dstAddr;
107 * Defines the CMTS to add to the PCMMService
112 * The class under test
114 private PCMMService service;
117 * The cable modem IP address to which a gate should be set
119 private InetAddress cmAddrInet;
120 private InetAddress invalidCmAddrInet;
123 public void setup() throws IOException {
124 srcAddr = new Ipv4Address("10.10.10.0");
125 dstAddr = new Ipv4Address("10.32.99.99");
126 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
129 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)172});
132 // Use me when testing against a CMTS or emulator not running in the same JVM
133 cmtsAddr = new Ipv4Address("10.32.10.3");
134 ccap = makeCcapObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId);
136 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
138 // Use me for automated testing and the CMTS emulator running in the same JVM
139 cmtsAddr = new Ipv4Address("127.0.0.1");
141 final Set<String> upSCN = new HashSet<>();
142 upSCN.add("extrm_up");
143 final Set<String> dnSCN = new HashSet<>();
144 dnSCN.add("extrm_dn");
146 final Map<String, Boolean> cmStatus = new HashMap<>();
147 cmStatus.put(cmAddrInet.getHostAddress(), true);
148 cmStatus.put(invalidCmAddrInet.getHostAddress(), false);
150 CMTSConfig config = new CMTSConfig(0, (short)4, upSCN, dnSCN, cmStatus);
152 icmts = new CMTS(config);
155 ccap = makeCcapObj(icmts.getPort(), cmtsAddr, ccapId);
158 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
162 public void tearDown() {
163 if (icmts != null) icmts.stopServer();
168 public void testAddCcap() {
169 connectToCmts(service);
173 public void testAddInvalidCcapBadPort() {
175 if (icmts != null) port = icmts.getPort() + 1;
176 else port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT + 1;
177 ccap = makeCcapObj(port, cmtsAddr, ccapId);
178 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
179 final String message = service.addCcap();
180 Assert.assertNotNull(message);
181 final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
182 + ':' + port + " - ";
183 Assert.assertTrue(expectedMsg, message.startsWith(expectedMsg));
187 public void testAddValidUpGateTwice() throws Exception {
188 connectToCmts(service);
189 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
190 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
193 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
194 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
197 Assert.assertTrue(deleteGate(service, gatePath));
201 public void testAddTwoValidUpGates() throws Exception {
202 connectToCmts(service);
204 final String gatePath1 = "gatePath1";
205 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath1 + " returned GateId";
206 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath1,
209 final String gatePath2 = "gatePath2";
210 final String expectedMsg2 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath2 + " returned GateId";
211 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath2,
214 Assert.assertTrue(deleteGate(service, gatePath1));
215 Assert.assertTrue(deleteGate(service, gatePath2));
219 public void testAddValidDownGateTwice() throws Exception {
220 connectToCmts(service);
221 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
222 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
225 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
226 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
229 Assert.assertTrue(deleteGate(service, gatePath));
233 public void testDeleteNonExistentGate() throws Exception {
234 connectToCmts(service);
235 Assert.assertFalse(deleteGate(service, gatePath));
239 public void testAddAndRemoveValidUpGate() throws Exception {
240 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
241 addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
246 public void testAddAndRemoveValidDownGate() throws Exception {
247 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
248 addRemoveValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
253 public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception {
254 // TODO - fix cmts emulator
255 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
256 + " returned error - Error Code: 13 Subcode: 0 Invalid SubscriberID";
257 addInvalidGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet, gatePath,
262 public void testAddInvalidScnUpGate() throws Exception {
263 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
264 + " returned error - Error Code: 11 Subcode: 0 Undefined Service Class Name";
265 addInvalidGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
270 public void testAddInvalidScnDownGate() throws Exception {
271 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
272 + " returned error - Error Code: 11 Subcode: 0 Undefined Service Class Name";
273 addInvalidGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
278 * This tests the instantiation of a COPSDecisionMsg object that is responsible for setting a gate request,
279 * streams it over a mock Socket object and parses the bytes into a new COPSDecisionMsg object which should
281 * @throws Exception - test will fail should any exception be thrown during execution
284 public void testGateRequestDecisionMsg() throws Exception {
285 final Socket socket = new MockSocket();
287 final ServiceFlowDirection direction = ServiceFlowDirection.Us;
288 final Gate gate = makeGateObj("extrm_up", cmtsAddr, direction, new Ipv4Address("127.0.0.1"));
289 final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"), direction);
290 final byte[] data = gateReq.getData();
292 final Set<COPSDecision> decisionSet = new HashSet<>();
293 decisionSet.add(new COPSDecision(CType.DEF, Command.INSTALL, DecisionFlag.REQERROR));
294 final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
295 decisionMap.put(new COPSContext(RType.CONFIG, (short) 0), decisionSet);
297 final COPSClientSI clientSD = new COPSClientSI(CNum.DEC, CType.CSI, new COPSData(data, 0, data.length));
298 final COPSDecisionMsg decisionMsg = new COPSDecisionMsg(IPCMMClient.CLIENT_TYPE, new COPSHandle(new COPSData("123")),
299 decisionMap, null, clientSD);
300 decisionMsg.writeData(socket);
302 final COPSMsg msg = COPSMsgParser.parseMessage(socket);
303 Assert.assertNotNull(msg);
304 Assert.assertEquals(decisionMsg, msg);
308 * Attempts to create a gate against a CMTS, validates the results then attempts to delete it.
309 * @param service - the service used to connect to a CMTS for issuing requests
310 * @param scnName - the service class name (aka. gate name)
311 * @param srcAddr - the address to the CMTS subnet?
312 * @param dstAddr - the destination address
313 * @param direction - the gate direction
314 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
315 * @param gatePath - the path to the gate
316 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
318 private void addRemoveValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
319 final Ipv4Address dstAddr, final ServiceFlowDirection direction,
320 final InetAddress cmAddrInet, final String gatePath,
321 final String expGateSetMsgStart) {
322 connectToCmts(service);
323 addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
324 deleteGate(service, gatePath);
327 private void addInvalidGate(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 connectToCmts(service);
332 final int numRequestsBefore = service.gateRequests.size();
333 addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
334 assertEquals(numRequestsBefore, service.gateRequests.size());
337 private void connectToCmts(final PCMMService service) {
338 final String message = service.addCcap();
339 Assert.assertNotNull(message);
340 final String expectedMsg = "200 OK - CCAP " + ccapId + " connected @ "
341 + ccap.getConnection().getIpAddress().getIpv4Address().getValue()
342 + ":" + ccap.getConnection().getPort().getValue();
343 Assert.assertEquals(expectedMsg, message);
344 Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
348 * Attempts to create a gate against a CMTS and validates the results.
349 * @param service - the service used to connect to a CMTS for issuing requests
350 * @param scnName - the service class name (aka. gate name)
351 * @param srcAddr - the address to the CMTS subnet?
352 * @param dstAddr - the destination address
353 * @param direction - the gate direction
354 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
355 * @param gatePath - the path to the gate
356 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
358 private void addAndValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
359 final Ipv4Address dstAddr, final ServiceFlowDirection direction,
360 final InetAddress cmAddrInet, final String gatePath,
361 final String expGateSetMsgStart) {
362 final Gate gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
364 // final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
365 // Assert.assertNotNull(gateSetMsg);
366 // Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
368 // TODO update this method for the new GateSendStatus object
369 PCMMService.GateSendStatus status = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
370 Assert.assertNotNull(status);
371 assertThat(status.getMessage(), startsWith(expGateSetMsgStart));
374 // TODO - add validation to the PCMMGateReq contained within the map
375 if (status.didSucceed()) {
376 Assert.assertTrue((service.gateRequests.containsKey(gatePath)));
377 Assert.assertNotNull(service.gateRequests.get(gatePath));
382 * Attempts to delete a gate
383 * @param service - the service used to connect to a CMTS for issuing requests
384 * @param gatePath - the path to the gate
386 private boolean deleteGate(final PCMMService service, final String gatePath) {
387 final boolean out = service.sendGateDelete(gatePath);
389 // Wait up to 1 sec for response to be processed
390 final long start = System.currentTimeMillis();
391 while (1000 < System.currentTimeMillis() - start) {
392 if (service.gateRequests.size() == 0) break;
394 Assert.assertNull(service.gateRequests.get(gatePath));
399 * Creates a mock Ccap object that can be used for connecting to a CMTS
400 * @param inPort - the CMTS port number
401 * @param ipAddr - the CMTS IPv4 address string
402 * @param ccapId - the ID of the CCAP
403 * @return - the mock Ccap object
405 private Ccap makeCcapObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
406 final Ccap ccap = mock(Ccap.class);
407 final Connection conn = mock(Connection.class);
408 when(ccap.getConnection()).thenReturn(conn);
409 final PortNumber port = mock(PortNumber.class);
410 when(conn.getPort()).thenReturn(port);
411 when(port.getValue()).thenReturn(inPort);
413 final IpAddress addr = mock(IpAddress.class);
414 when(conn.getIpAddress()).thenReturn(addr);
415 when(addr.getIpv4Address()).thenReturn(ipAddr);
417 when(ccap.getCcapId()).thenReturn(ccapId);
418 final AmId amid = mock(AmId.class);
419 when(ccap.getAmId()).thenReturn(amid);
420 when(amid.getAmTag()).thenReturn(0xcada);
421 when(amid.getAmType()).thenReturn(1);
427 * Creates a mock Gate object
428 * @param scnValue - the service class name defined on the CMTS
429 * @param dstAddr - the CM address this gate should be set against
430 * @return - the gate request
432 private Gate makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
433 final Ipv4Address dstAddr) {
434 final Gate gate = mock(Gate.class);
435 final GateSpec gateSpec = mock(GateSpec.class);
436 when(gate.getGateSpec()).thenReturn(gateSpec);
437 when(gateSpec.getDirection()).thenReturn(direction);
438 // TODO - make sure to write a test when this value is not null
439 when(gateSpec.getDscpTosOverwrite()).thenReturn(null);
440 final TrafficProfile trafficProfile = mock(TrafficProfile.class);
441 final ServiceClassName scn = mock(ServiceClassName.class);
442 when(scn.getValue()).thenReturn(scnValue);
443 when(trafficProfile.getServiceClassName()).thenReturn(scn);
444 when(gate.getTrafficProfile()).thenReturn(trafficProfile);
446 // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
447 final Classifier classifier = mock(Classifier.class);
449 // This is the address of the CM
450 when(classifier.getDstIp()).thenReturn(dstAddr);
452 final PortNumber dstPort = new PortNumber(4321);
453 when(classifier.getDstPort()).thenReturn(dstPort);
454 final TpProtocol protocol = new TpProtocol(0);
455 when(classifier.getProtocol()).thenReturn(protocol);
456 when(classifier.getSrcIp()).thenReturn(srcAddr);
457 final PortNumber srcPort = new PortNumber(1234);
458 when(classifier.getSrcPort()).thenReturn(srcPort);
460 // TODO - Can this value be any other value than 0 or 1 (See TosByte enumeration)
461 final TosByte tosByte = new TosByte((short)0);
462 when(classifier.getTosByte()).thenReturn(tosByte);
463 final TosByte tosMask = new TosByte((short)224);
464 when(classifier.getTosMask()).thenReturn(tosMask);
466 final QosClassifierChoice classifierChoice = mock(QosClassifierChoice.class);
467 when(classifierChoice.getClassifier()).thenReturn(classifier);
469 ClassifierContainer classifierContainer = mock(ClassifierContainer.class);
470 when(classifierContainer.getClassifierChoice()).thenReturn(classifierChoice);
472 final List<ClassifierContainer> containerList = Collections.singletonList(classifierContainer);
474 Classifiers classifiers = mock(Classifiers.class);
475 when(classifiers.getClassifierContainer()).thenReturn(containerList);
477 when(gate.getClassifiers()).thenReturn(classifiers);
482 private IPCMMGate makeGateRequest(final Ccap ccap, final Gate gateReq, final InetAddress addrSubId,
483 final ServiceFlowDirection direction) {
484 final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
485 gateBuilder.setAmId(ccap.getAmId());
486 gateBuilder.setSubscriberId(addrSubId);
487 // force gateSpec.Direction to align with SCN direction
488 final ServiceClassName scn = gateReq.getTrafficProfile().getServiceClassName();
490 gateBuilder.setGateSpec(gateReq.getGateSpec(), direction);
493 gateBuilder.setGateSpec(gateReq.getGateSpec(), null);
495 gateBuilder.setTrafficProfile(gateReq.getTrafficProfile());
497 gateBuilder.setClassifiers(gateReq.getClassifiers().getClassifierContainer());
499 // assemble the final gate request
500 return gateBuilder.build();
503 private class MockSocket extends Socket {
505 private ByteArrayOutputStream os = new ByteArrayOutputStream();
506 private ByteArrayInputStream is;
509 public OutputStream getOutputStream() {
514 public InputStream getInputStream() {
515 if (is == null) is = new ByteArrayInputStream(os.toByteArray());