2 * (c) 2015 Cable Television Laboratories, Inc. All rights reserved.
5 package org.opendaylight.controller.packetcable.provider;
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;
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;
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.
61 public class PCMMServiceTest {
63 private final static String ccapId = "ccap-1";
64 private final static String gatePath = "testGatePath";
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.
70 private final static boolean realCmts = false;
73 // The test objects/values to use that will be instantiated in @Before
76 * The mock CMTS running on localhost with a dynamic port assigned.
81 * The IP address object for the CMTS to test against
83 private Ipv4Address cmtsAddr;
86 * The gate classifier's srcIp value, any valid IP should work.
88 private Ipv4Address srcAddr;
91 * The gate classifier's dstIp value, any valid IP should work.
93 private Ipv4Address dstAddr;
96 * Defines the CMTS to add to the PCMMService
101 * The class under test
103 private PCMMService service;
106 * The cable modem IP address to which a gate should be set
108 private InetAddress cmAddrInet;
109 private InetAddress invalidCmAddrInet;
112 public void setup() throws IOException {
113 srcAddr = new Ipv4Address("10.10.10.0");
114 dstAddr = new Ipv4Address("10.32.99.99");
117 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)172});
118 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
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);
124 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
125 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
127 // Use me for automated testing and the CMTS emulator running in the same JVM
128 cmtsAddr = new Ipv4Address("127.0.0.1");
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);
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);
144 ccap = makeCcapObj(icmts.getPort(), cmtsAddr, ccapId);
147 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
151 public void tearDown() {
152 if (icmts != null) icmts.stopServer();
157 public void testAddCcap() {
158 connectToCmts(service);
162 public void testAddInvalidCcapBadPort() {
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));
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,
182 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
183 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
186 Assert.assertTrue(deleteGate(service, gatePath));
190 public void testAddTwoValidUpGates() throws Exception {
191 connectToCmts(service);
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,
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,
203 Assert.assertTrue(deleteGate(service, gatePath1));
204 Assert.assertTrue(deleteGate(service, gatePath2));
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,
214 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
215 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
218 Assert.assertTrue(deleteGate(service, gatePath));
222 public void testDeleteNonExistentGate() throws Exception {
223 connectToCmts(service);
224 Assert.assertFalse(deleteGate(service, gatePath));
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,
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,
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);
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);
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);
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
270 * @throws Exception - test will fail should any exception be thrown during execution
273 public void testGateRequestDecisionMsg() throws Exception {
274 final Socket socket = new MockSocket();
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();
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);
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);
291 final COPSMsg msg = COPSMsgParser.parseMessage(socket);
292 Assert.assertNotNull(msg);
293 Assert.assertEquals(decisionMsg, msg);
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
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);
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());
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
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);
343 final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
344 Assert.assertNotNull(gateSetMsg);
345 Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
347 // TODO - add validation to the PCMMGateReq contained within the map
348 Assert.assertNotNull(service.gateRequests.get(gatePath));
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
356 private boolean deleteGate(final PCMMService service, final String gatePath) {
357 final boolean out = service.sendGateDelete(gatePath);
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;
364 Assert.assertNull(service.gateRequests.get(gatePath));
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
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);
383 final IpAddress addr = Mockito.mock(IpAddress.class);
384 Mockito.when(conn.getIpAddress()).thenReturn(addr);
385 Mockito.when(addr.getIpv4Address()).thenReturn(ipAddr);
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);
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
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);
416 // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
417 final Classifier classifier = Mockito.mock(Classifier.class);
419 // This is the address of the CM
420 Mockito.when(classifier.getDstIp()).thenReturn(dstAddr);
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);
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);
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);
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();
451 gateBuilder.setGateSpec(gateReq.getGateSpec(), direction);
454 gateBuilder.setGateSpec(gateReq.getGateSpec(), null);
456 gateBuilder.setTrafficProfile(gateReq.getTrafficProfile());
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());
466 // assemble the final gate request
467 return gateBuilder.build();
470 private class MockSocket extends Socket {
472 private ByteArrayOutputStream os = new ByteArrayOutputStream();
473 private ByteArrayInputStream is;
476 public OutputStream getOutputStream() {
481 public InputStream getInputStream() {
482 if (is == null) is = new ByteArrayInputStream(os.toByteArray());