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 srcAddr = new Ipv4Address("10.10.10.0");
104 dstAddr = new Ipv4Address("10.32.99.99");
107 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
108 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
110 // Use me when testing against a CMTS or emulator not running in the same JVM
111 cmtsAddr = new Ipv4Address("10.32.10.3");
112 ccap = makeCcapsObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId);
114 cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180});
115 invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99});
117 // Use me for automated testing and the CMTS emulator running in the same JVM
118 cmtsAddr = new Ipv4Address("127.0.0.1");
120 final Set<String> upGates = new HashSet<>();
121 upGates.add("extrm_up");
122 final Set<String> dnGates = new HashSet<>();
123 dnGates.add("extrm_dn");
124 final Map<Direction, Set<String>> gates = new HashMap<>();
125 gates.put(Direction.UPSTREAM, upGates);
126 gates.put(Direction.DOWNSTREAM, dnGates);
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 testAddInvalidCcapBadPort() {
153 ccap = makeCcapsObj((icmts.getPort() + 1), cmtsAddr, ccapId);
154 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
155 final String message = service.addCcap();
156 Assert.assertNotNull(message);
157 final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
158 + ':' + (icmts.getPort() + 1) + " - ";
159 Assert.assertTrue(expectedMsg, message.startsWith(expectedMsg));
163 public void testAddValidUpGateTwice() throws Exception {
164 connectToCmts(service);
165 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
166 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
169 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
170 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
173 Assert.assertTrue(deleteGate(service, gatePath));
177 public void testAddTwoValidUpGates() throws Exception {
178 connectToCmts(service);
180 final String gatePath1 = "gatePath1";
181 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath1 + " returned GateId";
182 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath1,
185 final String gatePath2 = "gatePath2";
186 final String expectedMsg2 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath2 + " returned GateId";
187 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath2,
190 Assert.assertTrue(deleteGate(service, gatePath1));
191 Assert.assertTrue(deleteGate(service, gatePath2));
195 public void testAddValidDownGateTwice() throws Exception {
196 connectToCmts(service);
197 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
198 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
201 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
202 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
205 Assert.assertTrue(deleteGate(service, gatePath));
209 public void testDeleteNonExistentGate() throws Exception {
210 connectToCmts(service);
211 Assert.assertFalse(deleteGate(service, gatePath));
215 public void testAddAndRemoveValidUpGate() throws Exception {
216 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
217 addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
222 public void testAddAndRemoveValidDownGate() throws Exception {
223 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
224 addRemoveValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
229 public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception {
230 // TODO - fix cmts emulator
231 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
232 + " returned error - Error Code: 13 Error Subcode : 0 Invalid SubscriberID";
233 addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet,
234 gatePath, expectedMsgStart);
238 public void testAddInvalidScnUpGate() throws Exception {
239 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
240 + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name";
241 addRemoveValidateGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet,
242 gatePath, expectedMsgStart);
246 public void testAddInvalidScnDownGate() throws Exception {
247 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
248 + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name";
249 addRemoveValidateGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet,
250 gatePath, expectedMsgStart);
254 * This tests the instantiation of a COPSDecisionMsg object that is responsible for setting a gate request,
255 * streams it over a mock Socket object and parses the bytes into a new COPSDecisionMsg object which should
257 * @throws Exception - test will fail should any exception be thrown during execution
260 public void testGateRequestDecisionMsg() throws Exception {
261 final Socket socket = new MockSocket();
263 final ServiceFlowDirection direction = ServiceFlowDirection.Us;
264 final Gates gate = makeGateObj("extrm_up", cmtsAddr, direction, new Ipv4Address("127.0.0.1"));
265 final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"), direction);
266 final byte[] data = gateReq.getData();
268 final Set<COPSDecision> decisionSet = new HashSet<>();
269 decisionSet.add(new COPSDecision(CType.DEF, Command.INSTALL, DecisionFlag.REQERROR));
270 final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
271 decisionMap.put(new COPSContext(RType.CONFIG, (short) 0), decisionSet);
273 final COPSClientSI clientSD = new COPSClientSI(CNum.DEC, CType.CSI, new COPSData(data, 0, data.length));
274 final COPSDecisionMsg decisionMsg = new COPSDecisionMsg(IPCMMClient.CLIENT_TYPE, new COPSHandle(new COPSData("123")),
275 decisionMap, null, clientSD);
276 decisionMsg.writeData(socket);
278 final COPSMsg msg = COPSMsgParser.parseMessage(socket);
279 Assert.assertNotNull(msg);
280 Assert.assertEquals(decisionMsg, msg);
284 * Attempts to create a gate against a CMTS, validates the results then attempts to delete it.
285 * @param service - the service used to connect to a CMTS for issuing requests
286 * @param scnName - the service class name (aka. gate name)
287 * @param srcAddr - the address to the CMTS subnet?
288 * @param dstAddr - the destination address
289 * @param direction - the gate direction
290 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
291 * @param gatePath - the path to the gate
292 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
294 private void addRemoveValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
295 final Ipv4Address dstAddr, final ServiceFlowDirection direction,
296 final InetAddress cmAddrInet, final String gatePath,
297 final String expGateSetMsgStart) {
298 connectToCmts(service);
299 addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
300 deleteGate(service, gatePath);
303 private void connectToCmts(final PCMMService service) {
304 final String message = service.addCcap();
305 Assert.assertNotNull(message);
306 final String expectedMsg = "200 OK - CCAP " + ccapId + " connected @ "
307 + ccap.getConnection().getIpAddress().getIpv4Address().getValue()
308 + ":" + ccap.getConnection().getPort().getValue();
309 Assert.assertEquals(expectedMsg, message);
310 Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
314 * Attempts to create a gate against a CMTS and validates the results.
315 * @param service - the service used to connect to a CMTS for issuing requests
316 * @param scnName - the service class name (aka. gate name)
317 * @param srcAddr - the address to the CMTS subnet?
318 * @param dstAddr - the destination address
319 * @param direction - the gate direction
320 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
321 * @param gatePath - the path to the gate
322 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
324 private void addAndValidateGate(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 final Gates gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
330 final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
331 Assert.assertNotNull(gateSetMsg);
332 Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
334 // TODO - add validation to the PCMMGateReq contained within the map
335 Assert.assertNotNull(service.gateRequests.get(gatePath));
339 * Attempts to delete a gate
340 * @param service - the service used to connect to a CMTS for issuing requests
341 * @param gatePath - the path to the gate
343 private boolean deleteGate(final PCMMService service, final String gatePath) {
344 final boolean out = service.sendGateDelete(gatePath);
346 // Wait up to 1 sec for response to be processed
347 final long start = System.currentTimeMillis();
348 while (1000 < System.currentTimeMillis() - start) {
349 if (service.gateRequests.size() == 0) break;
351 Assert.assertNull(service.gateRequests.get(gatePath));
356 * Creates a mock Ccaps object that can be used for connecting to a CMTS
357 * @param inPort - the CMTS port number
358 * @param ipAddr - the CMTS IPv4 address string
359 * @param ccapId - the ID of the CCAP
360 * @return - the mock Ccaps object
362 private Ccaps makeCcapsObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
363 final Ccaps ccap = Mockito.mock(Ccaps.class);
364 final Connection conn = Mockito.mock(Connection.class);
365 Mockito.when(ccap.getConnection()).thenReturn(conn);
366 final PortNumber port = Mockito.mock(PortNumber.class);
367 Mockito.when(conn.getPort()).thenReturn(port);
368 Mockito.when(port.getValue()).thenReturn(inPort);
370 final IpAddress addr = Mockito.mock(IpAddress.class);
371 Mockito.when(conn.getIpAddress()).thenReturn(addr);
372 Mockito.when(addr.getIpv4Address()).thenReturn(ipAddr);
374 Mockito.when(ccap.getCcapId()).thenReturn(ccapId);
375 final AmId amid = Mockito.mock(AmId.class);
376 Mockito.when(ccap.getAmId()).thenReturn(amid);
377 Mockito.when(amid.getAmTag()).thenReturn(0xcada);
378 Mockito.when(amid.getAmType()).thenReturn(1);
384 * Creates a mock Gates object
385 * @param scnValue - the service class name defined on the CMTS
386 * @param dstAddr - the CM address this gate should be set against
387 * @return - the gate request
389 private Gates makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
390 final Ipv4Address dstAddr) {
391 final Gates gate = Mockito.mock(Gates.class);
392 final GateSpec gateSpec = Mockito.mock(GateSpec.class);
393 Mockito.when(gate.getGateSpec()).thenReturn(gateSpec);
394 Mockito.when(gateSpec.getDirection()).thenReturn(direction);
395 // TODO - make sure to write a test when this value is not null
396 Mockito.when(gateSpec.getDscpTosOverwrite()).thenReturn(null);
397 final TrafficProfile trafficProfile = Mockito.mock(TrafficProfile.class);
398 final ServiceClassName scn = Mockito.mock(ServiceClassName.class);
399 Mockito.when(scn.getValue()).thenReturn(scnValue);
400 Mockito.when(trafficProfile.getServiceClassName()).thenReturn(scn);
401 Mockito.when(gate.getTrafficProfile()).thenReturn(trafficProfile);
403 // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
404 final Classifier classifier = Mockito.mock(Classifier.class);
406 // This is the address of the CM
407 Mockito.when(classifier.getDstIp()).thenReturn(dstAddr);
409 final PortNumber dstPort = new PortNumber(4321);
410 Mockito.when(classifier.getDstPort()).thenReturn(dstPort);
411 final TpProtocol protocol = new TpProtocol(0);
412 Mockito.when(classifier.getProtocol()).thenReturn(protocol);
413 Mockito.when(classifier.getSrcIp()).thenReturn(srcAddr);
414 final PortNumber srcPort = new PortNumber(1234);
415 Mockito.when(classifier.getSrcPort()).thenReturn(srcPort);
416 final TosByte tosByte = new TosByte((short)160);
417 Mockito.when(classifier.getTosByte()).thenReturn(tosByte);
418 final TosByte tosMask = new TosByte((short)224);
419 Mockito.when(classifier.getTosMask()).thenReturn(tosMask);
421 // TODO - enhance to test support of the other classifier types
422 Mockito.when(gate.getClassifier()).thenReturn(classifier);
423 Mockito.when(gate.getExtClassifier()).thenReturn(null);
424 Mockito.when(gate.getIpv6Classifier()).thenReturn(null);
428 private IPCMMGate makeGateRequest(final Ccaps ccap, final Gates gateReq, final InetAddress addrSubId,
429 final ServiceFlowDirection direction) {
430 final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
431 gateBuilder.build(ccap.getAmId());
432 gateBuilder.build(addrSubId);
433 // force gateSpec.Direction to align with SCN direction
434 final ServiceClassName scn = gateReq.getTrafficProfile().getServiceClassName();
436 gateBuilder.build(gateReq.getGateSpec(), direction);
439 gateBuilder.build(gateReq.getGateSpec(), null);
441 gateBuilder.build(gateReq.getTrafficProfile());
443 // pick a classifier type (only one for now)
444 if (gateReq.getClassifier() != null) {
445 gateBuilder.build(gateReq.getClassifier());
446 } else if (gateReq.getExtClassifier() != null) {
447 gateBuilder.build(gateReq.getExtClassifier());
448 } else if (gateReq.getIpv6Classifier() != null) {
449 gateBuilder.build(gateReq.getIpv6Classifier());
451 // assemble the final gate request
452 return gateBuilder.getGateReq();
455 private class MockSocket extends Socket {
457 private ByteArrayOutputStream os = new ByteArrayOutputStream();
458 private ByteArrayInputStream is;
461 public OutputStream getOutputStream() {
466 public InputStream getInputStream() {
467 if (is == null) is = new ByteArrayInputStream(os.toByteArray());