2 * (c) 2015 Cable Television Laboratories, Inc. All rights reserved.
5 package org.opendaylight.controller.packetcable.provider;
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;
39 import java.net.InetAddress;
40 import java.net.Socket;
41 import java.util.HashMap;
42 import java.util.HashSet;
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.
51 public class PCMMServiceTest {
53 private final static String ccapId = "ccap-1";
54 private final static String gatePath = "testGatePath";
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.
60 private final static boolean realCmts = false;
63 // The test objects/values to use that will be instantiated in @Before
66 * The mock CMTS running on localhost with a dynamic port assigned.
71 * The IP address object for the CMTS to test against
73 private Ipv4Address cmtsAddr;
76 * The gate classifier's srcIp value, any valid IP should work.
78 private Ipv4Address srcAddr;
81 * The gate classifier's dstIp value, any valid IP should work.
83 private Ipv4Address dstAddr;
86 * Defines the CMTS to add to the PCMMService
91 * The class under test
93 private PCMMService service;
96 * The cable modem IP address to which a gate should be set
98 private InetAddress cmAddrInet;
99 private InetAddress invalidCmAddrInet;
102 public void setup() throws IOException {
103 final Set<String> upGates = new HashSet<>();
104 upGates.add("extrm_up");
105 final Set<String> dnGates = new HashSet<>();
106 dnGates.add("extrm_dn");
107 final Map<Direction, Set<String>> gates = new HashMap<>();
108 gates.put(Direction.UPSTREAM, upGates);
109 gates.put(Direction.DOWNSTREAM, dnGates);
111 srcAddr = new Ipv4Address("10.10.10.0");
112 dstAddr = new Ipv4Address("10.32.99.99");
115 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
116 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
118 // Use me when testing against a CMTS or emulator not running in the same JVM
119 cmtsAddr = new Ipv4Address("10.32.10.3");
120 ccap = makeCcapsObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId);
122 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
123 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
125 // Use me for automated testing and the CMTS emulator running in the same JVM
126 cmtsAddr = new Ipv4Address("127.0.0.1");
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);
134 ccap = makeCcapsObj(icmts.getPort(), cmtsAddr, ccapId);
137 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
141 public void tearDown() {
142 if (icmts != null) icmts.stopServer();
147 public void testAddCcap() {
148 connectToCmts(service);
152 public void testAddInvalidCcapBadHost() {
153 cmtsAddr = new Ipv4Address("0.0.0.0");
154 ccap = makeCcapsObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId);
155 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
156 final String message = service.addCcap();
157 Assert.assertNotNull(message);
158 final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
159 + ':' + PCMMPdpAgent.WELL_KNOWN_PDP_PORT + " - Connection refused";
160 Assert.assertEquals(expectedMsg, message);
164 public void testAddInvalidCcapBadPort() {
165 ccap = makeCcapsObj(1234, cmtsAddr, ccapId);
166 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
167 final String message = service.addCcap();
168 Assert.assertNotNull(message);
169 final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
170 + ":1234 - Connection refused";
171 Assert.assertEquals(expectedMsg, message);
175 public void testAddValidUpGateTwice() throws Exception {
176 connectToCmts(service);
177 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
178 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
181 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
182 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
185 Assert.assertTrue(deleteGate(service, gatePath));
189 public void testAddTwoValidUpGates() throws Exception {
190 connectToCmts(service);
192 final String gatePath1 = "gatePath1";
193 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath1 + " returned GateId";
194 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath1,
197 final String gatePath2 = "gatePath2";
198 final String expectedMsg2 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath2 + " returned GateId";
199 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath2,
202 Assert.assertTrue(deleteGate(service, gatePath1));
203 Assert.assertTrue(deleteGate(service, gatePath2));
207 public void testAddValidDownGateTwice() throws Exception {
208 connectToCmts(service);
209 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
210 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
213 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
214 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
217 Assert.assertTrue(deleteGate(service, gatePath));
221 public void testDeleteNonExistentGate() throws Exception {
222 connectToCmts(service);
223 Assert.assertFalse(deleteGate(service, gatePath));
227 public void testAddAndRemoveValidUpGate() throws Exception {
228 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
229 addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
234 public void testAddAndRemoveValidDownGate() throws Exception {
235 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
236 addRemoveValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
241 public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception {
242 // TODO - fix cmts emulator
243 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
244 + " returned error - Error Code: 13 Error Subcode : 0 Invalid SubscriberID";
245 addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet,
246 gatePath, expectedMsgStart);
250 public void testAddInvalidScnUpGate() throws Exception {
251 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
252 + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name";
253 addRemoveValidateGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet,
254 gatePath, expectedMsgStart);
258 public void testAddInvalidScnDownGate() throws Exception {
259 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
260 + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name";
261 addRemoveValidateGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet,
262 gatePath, expectedMsgStart);
266 * This tests the instantiation of a COPSDecisionMsg object that is responsible for setting a gate request,
267 * streams it over a mock Socket object and parses the bytes into a new COPSDecisionMsg object which should
269 * @throws Exception - test will fail should any exception be thrown during execution
272 public void testGateRequestDecisionMsg() throws Exception {
273 final Socket socket = new MockSocket();
275 final ServiceFlowDirection direction = ServiceFlowDirection.Us;
276 final Gates gate = makeGateObj("extrm_up", cmtsAddr, direction, new Ipv4Address("127.0.0.1"));
277 final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"), direction);
278 final byte[] data = gateReq.getData();
280 final Set<COPSDecision> decisionSet = new HashSet<>();
281 decisionSet.add(new COPSDecision(CType.DEF, Command.INSTALL, DecisionFlag.REQERROR));
282 final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
283 decisionMap.put(new COPSContext(RType.CONFIG, (short) 0), decisionSet);
285 final COPSClientSI clientSD = new COPSClientSI(CNum.DEC, CType.CSI, new COPSData(data, 0, data.length));
286 final COPSDecisionMsg decisionMsg = new COPSDecisionMsg(IPCMMClient.CLIENT_TYPE, new COPSHandle(new COPSData("123")),
287 decisionMap, null, clientSD);
288 decisionMsg.writeData(socket);
290 final COPSMsg msg = COPSMsgParser.parseMessage(socket);
291 Assert.assertNotNull(msg);
292 Assert.assertEquals(decisionMsg, msg);
296 * Attempts to create a gate against a CMTS, validates the results then attempts to delete it.
297 * @param service - the service used to connect to a CMTS for issuing requests
298 * @param scnName - the service class name (aka. gate name)
299 * @param srcAddr - the address to the CMTS subnet?
300 * @param dstAddr - the destination address
301 * @param direction - the gate direction
302 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
303 * @param gatePath - the path to the gate
304 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
306 private void addRemoveValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
307 final Ipv4Address dstAddr, final ServiceFlowDirection direction,
308 final InetAddress cmAddrInet, final String gatePath,
309 final String expGateSetMsgStart) {
310 connectToCmts(service);
311 addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
312 deleteGate(service, gatePath);
315 private void connectToCmts(final PCMMService service) {
316 final String message = service.addCcap();
317 Assert.assertNotNull(message);
318 final String expectedMsg = "200 OK - CCAP " + ccapId + " connected @ "
319 + ccap.getConnection().getIpAddress().getIpv4Address().getValue()
320 + ":" + ccap.getConnection().getPort().getValue();
321 Assert.assertEquals(expectedMsg, message);
322 Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
326 * Attempts to create a gate against a CMTS and validates the results.
327 * @param service - the service used to connect to a CMTS for issuing requests
328 * @param scnName - the service class name (aka. gate name)
329 * @param srcAddr - the address to the CMTS subnet?
330 * @param dstAddr - the destination address
331 * @param direction - the gate direction
332 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
333 * @param gatePath - the path to the gate
334 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
336 private void addAndValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
337 final Ipv4Address dstAddr, final ServiceFlowDirection direction,
338 final InetAddress cmAddrInet, final String gatePath,
339 final String expGateSetMsgStart) {
340 final Gates gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
342 final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
343 Assert.assertNotNull(gateSetMsg);
344 Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
346 // TODO - add validation to the PCMMGateReq contained within the map
347 Assert.assertNotNull(service.gateRequests.get(gatePath));
351 * Attempts to delete a gate
352 * @param service - the service used to connect to a CMTS for issuing requests
353 * @param gatePath - the path to the gate
355 private boolean deleteGate(final PCMMService service, final String gatePath) {
356 final boolean out = service.sendGateDelete(gatePath);
358 // Wait up to 1 sec for response to be processed
359 final long start = System.currentTimeMillis();
360 while (1000 < System.currentTimeMillis() - start) {
361 if (service.gateRequests.size() == 0) break;
363 Assert.assertNull(service.gateRequests.get(gatePath));
368 * Creates a mock Ccaps object that can be used for connecting to a CMTS
369 * @param inPort - the CMTS port number
370 * @param ipAddr - the CMTS IPv4 address string
371 * @param ccapId - the ID of the CCAP
372 * @return - the mock Ccaps object
374 private Ccaps makeCcapsObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
375 final Ccaps ccap = Mockito.mock(Ccaps.class);
376 final Connection conn = Mockito.mock(Connection.class);
377 Mockito.when(ccap.getConnection()).thenReturn(conn);
378 final PortNumber port = Mockito.mock(PortNumber.class);
379 Mockito.when(conn.getPort()).thenReturn(port);
380 Mockito.when(port.getValue()).thenReturn(inPort);
382 final IpAddress addr = Mockito.mock(IpAddress.class);
383 Mockito.when(conn.getIpAddress()).thenReturn(addr);
384 Mockito.when(addr.getIpv4Address()).thenReturn(ipAddr);
386 Mockito.when(ccap.getCcapId()).thenReturn(ccapId);
387 final AmId amid = Mockito.mock(AmId.class);
388 Mockito.when(ccap.getAmId()).thenReturn(amid);
389 Mockito.when(amid.getAmTag()).thenReturn(0xcada);
390 Mockito.when(amid.getAmType()).thenReturn(1);
396 * Creates a mock Gates object
397 * @param scnValue - the service class name defined on the CMTS
398 * @param dstAddr - the CM address this gate should be set against
399 * @return - the gate request
401 private Gates makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
402 final Ipv4Address dstAddr) {
403 final Gates gate = Mockito.mock(Gates.class);
404 final GateSpec gateSpec = Mockito.mock(GateSpec.class);
405 Mockito.when(gate.getGateSpec()).thenReturn(gateSpec);
406 Mockito.when(gateSpec.getDirection()).thenReturn(direction);
407 // TODO - make sure to write a test when this value is not null
408 Mockito.when(gateSpec.getDscpTosOverwrite()).thenReturn(null);
409 final TrafficProfile trafficProfile = Mockito.mock(TrafficProfile.class);
410 final ServiceClassName scn = Mockito.mock(ServiceClassName.class);
411 Mockito.when(scn.getValue()).thenReturn(scnValue);
412 Mockito.when(trafficProfile.getServiceClassName()).thenReturn(scn);
413 Mockito.when(gate.getTrafficProfile()).thenReturn(trafficProfile);
415 // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
416 final Classifier classifier = Mockito.mock(Classifier.class);
418 // This is the address of the CM
419 Mockito.when(classifier.getDstIp()).thenReturn(dstAddr);
421 final PortNumber dstPort = new PortNumber(4321);
422 Mockito.when(classifier.getDstPort()).thenReturn(dstPort);
423 final TpProtocol protocol = new TpProtocol(0);
424 Mockito.when(classifier.getProtocol()).thenReturn(protocol);
425 Mockito.when(classifier.getSrcIp()).thenReturn(srcAddr);
426 final PortNumber srcPort = new PortNumber(1234);
427 Mockito.when(classifier.getSrcPort()).thenReturn(srcPort);
428 final TosByte tosByte = new TosByte((short)160);
429 Mockito.when(classifier.getTosByte()).thenReturn(tosByte);
430 final TosByte tosMask = new TosByte((short)224);
431 Mockito.when(classifier.getTosMask()).thenReturn(tosMask);
433 // TODO - enhance to test support of the other classifier types
434 Mockito.when(gate.getClassifier()).thenReturn(classifier);
435 Mockito.when(gate.getExtClassifier()).thenReturn(null);
436 Mockito.when(gate.getIpv6Classifier()).thenReturn(null);
440 private IPCMMGate makeGateRequest(final Ccaps ccap, final Gates gateReq, final InetAddress addrSubId,
441 final ServiceFlowDirection direction) {
442 final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
443 gateBuilder.build(ccap.getAmId());
444 gateBuilder.build(addrSubId);
445 // force gateSpec.Direction to align with SCN direction
446 final ServiceClassName scn = gateReq.getTrafficProfile().getServiceClassName();
448 gateBuilder.build(gateReq.getGateSpec(), direction);
451 gateBuilder.build(gateReq.getGateSpec(), null);
453 gateBuilder.build(gateReq.getTrafficProfile());
455 // pick a classifier type (only one for now)
456 if (gateReq.getClassifier() != null) {
457 gateBuilder.build(gateReq.getClassifier());
458 } else if (gateReq.getExtClassifier() != null) {
459 gateBuilder.build(gateReq.getExtClassifier());
460 } else if (gateReq.getIpv6Classifier() != null) {
461 gateBuilder.build(gateReq.getIpv6Classifier());
463 // assemble the final gate request
464 return gateBuilder.getGateReq();
467 private class MockSocket extends Socket {
469 private ByteArrayOutputStream os = new ByteArrayOutputStream();
470 private ByteArrayInputStream is;
473 public OutputStream getOutputStream() {
478 public InputStream getInputStream() {
479 if (is == null) is = new ByteArrayInputStream(os.toByteArray());