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 update this method for the new GateSetStatus object
348 PCMMService.GateSetStatus status = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
349 Assert.assertNotNull(status);
350 Assert.assertTrue(status.getMessage().startsWith(expGateSetMsgStart));
352 // TODO - add validation to the PCMMGateReq contained within the map
353 Assert.assertNotNull(service.gateRequests.get(gatePath));
357 * Attempts to delete a gate
358 * @param service - the service used to connect to a CMTS for issuing requests
359 * @param gatePath - the path to the gate
361 private boolean deleteGate(final PCMMService service, final String gatePath) {
362 final boolean out = service.sendGateDelete(gatePath);
364 // Wait up to 1 sec for response to be processed
365 final long start = System.currentTimeMillis();
366 while (1000 < System.currentTimeMillis() - start) {
367 if (service.gateRequests.size() == 0) break;
369 Assert.assertNull(service.gateRequests.get(gatePath));
374 * Creates a mock Ccap object that can be used for connecting to a CMTS
375 * @param inPort - the CMTS port number
376 * @param ipAddr - the CMTS IPv4 address string
377 * @param ccapId - the ID of the CCAP
378 * @return - the mock Ccap object
380 private Ccap makeCcapObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
381 final Ccap ccap = Mockito.mock(Ccap.class);
382 final Connection conn = Mockito.mock(Connection.class);
383 Mockito.when(ccap.getConnection()).thenReturn(conn);
384 final PortNumber port = Mockito.mock(PortNumber.class);
385 Mockito.when(conn.getPort()).thenReturn(port);
386 Mockito.when(port.getValue()).thenReturn(inPort);
388 final IpAddress addr = Mockito.mock(IpAddress.class);
389 Mockito.when(conn.getIpAddress()).thenReturn(addr);
390 Mockito.when(addr.getIpv4Address()).thenReturn(ipAddr);
392 Mockito.when(ccap.getCcapId()).thenReturn(ccapId);
393 final AmId amid = Mockito.mock(AmId.class);
394 Mockito.when(ccap.getAmId()).thenReturn(amid);
395 Mockito.when(amid.getAmTag()).thenReturn(0xcada);
396 Mockito.when(amid.getAmType()).thenReturn(1);
402 * Creates a mock Gate object
403 * @param scnValue - the service class name defined on the CMTS
404 * @param dstAddr - the CM address this gate should be set against
405 * @return - the gate request
407 private Gate makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
408 final Ipv4Address dstAddr) {
409 final Gate gate = Mockito.mock(Gate.class);
410 final GateSpec gateSpec = Mockito.mock(GateSpec.class);
411 Mockito.when(gate.getGateSpec()).thenReturn(gateSpec);
412 Mockito.when(gateSpec.getDirection()).thenReturn(direction);
413 // TODO - make sure to write a test when this value is not null
414 Mockito.when(gateSpec.getDscpTosOverwrite()).thenReturn(null);
415 final TrafficProfile trafficProfile = Mockito.mock(TrafficProfile.class);
416 final ServiceClassName scn = Mockito.mock(ServiceClassName.class);
417 Mockito.when(scn.getValue()).thenReturn(scnValue);
418 Mockito.when(trafficProfile.getServiceClassName()).thenReturn(scn);
419 Mockito.when(gate.getTrafficProfile()).thenReturn(trafficProfile);
421 // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
422 final Classifier classifier = Mockito.mock(Classifier.class);
424 // This is the address of the CM
425 Mockito.when(classifier.getDstIp()).thenReturn(dstAddr);
427 final PortNumber dstPort = new PortNumber(4321);
428 Mockito.when(classifier.getDstPort()).thenReturn(dstPort);
429 final TpProtocol protocol = new TpProtocol(0);
430 Mockito.when(classifier.getProtocol()).thenReturn(protocol);
431 Mockito.when(classifier.getSrcIp()).thenReturn(srcAddr);
432 final PortNumber srcPort = new PortNumber(1234);
433 Mockito.when(classifier.getSrcPort()).thenReturn(srcPort);
435 // TODO - Can this value be any other value than 0 or 1 (See TosByte enumeration)
436 final TosByte tosByte = new TosByte((short)0);
437 Mockito.when(classifier.getTosByte()).thenReturn(tosByte);
438 final TosByte tosMask = new TosByte((short)224);
439 Mockito.when(classifier.getTosMask()).thenReturn(tosMask);
441 // TODO - enhance to test support of the other classifier types
442 Mockito.when(gate.getClassifier()).thenReturn(classifier);
443 Mockito.when(gate.getExtClassifier()).thenReturn(null);
444 Mockito.when(gate.getIpv6Classifier()).thenReturn(null);
448 private IPCMMGate makeGateRequest(final Ccap ccap, final Gate gateReq, final InetAddress addrSubId,
449 final ServiceFlowDirection direction) {
450 final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
451 gateBuilder.setAmId(ccap.getAmId());
452 gateBuilder.setSubscriberId(addrSubId);
453 // force gateSpec.Direction to align with SCN direction
454 final ServiceClassName scn = gateReq.getTrafficProfile().getServiceClassName();
456 gateBuilder.setGateSpec(gateReq.getGateSpec(), direction);
459 gateBuilder.setGateSpec(gateReq.getGateSpec(), null);
461 gateBuilder.setTrafficProfile(gateReq.getTrafficProfile());
463 // pick a classifier type (only one for now)
464 if (gateReq.getClassifier() != null) {
465 gateBuilder.setClassifier(gateReq.getClassifier());
466 } else if (gateReq.getExtClassifier() != null) {
467 gateBuilder.setExtClassifier(gateReq.getExtClassifier());
468 } else if (gateReq.getIpv6Classifier() != null) {
469 gateBuilder.setIpv6Classifier(gateReq.getIpv6Classifier());
471 // assemble the final gate request
472 return gateBuilder.build();
475 private class MockSocket extends Socket {
477 private ByteArrayOutputStream os = new ByteArrayOutputStream();
478 private ByteArrayInputStream is;
481 public OutputStream getOutputStream() {
486 public InputStream getInputStream() {
487 if (is == null) is = new ByteArrayInputStream(os.toByteArray());