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)172});
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() {
154 if (icmts != null) port = icmts.getPort() + 1;
155 else port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT + 1;
156 ccap = makeCcapsObj(port, cmtsAddr, ccapId);
157 service = new PCMMService(IPCMMClient.CLIENT_TYPE, ccap);
158 final String message = service.addCcap();
159 Assert.assertNotNull(message);
160 final String expectedMsg = "404 Not Found - CCAP " + ccapId + " failed to connect @ " + cmtsAddr.getValue()
161 + ':' + port + " - ";
162 Assert.assertTrue(expectedMsg, message.startsWith(expectedMsg));
166 public void testAddValidUpGateTwice() throws Exception {
167 connectToCmts(service);
168 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
169 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
172 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
173 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
176 Assert.assertTrue(deleteGate(service, gatePath));
180 public void testAddTwoValidUpGates() throws Exception {
181 connectToCmts(service);
183 final String gatePath1 = "gatePath1";
184 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath1 + " returned GateId";
185 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath1,
188 final String gatePath2 = "gatePath2";
189 final String expectedMsg2 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath2 + " returned GateId";
190 addAndValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath2,
193 Assert.assertTrue(deleteGate(service, gatePath1));
194 Assert.assertTrue(deleteGate(service, gatePath2));
198 public void testAddValidDownGateTwice() throws Exception {
199 connectToCmts(service);
200 final String expectedMsg1 = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
201 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
204 final String expectedMsg2 = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath + " already exists";
205 addAndValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
208 Assert.assertTrue(deleteGate(service, gatePath));
212 public void testDeleteNonExistentGate() throws Exception {
213 connectToCmts(service);
214 Assert.assertFalse(deleteGate(service, gatePath));
218 public void testAddAndRemoveValidUpGate() throws Exception {
219 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
220 addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath,
225 public void testAddAndRemoveValidDownGate() throws Exception {
226 final String expectedMsgStart = "200 OK - sendGateSet for " + ccapId + '/' + gatePath + " returned GateId";
227 addRemoveValidateGate(service, "extrm_dn", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath,
232 public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception {
233 // TODO - fix cmts emulator
234 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
235 + " returned error - Error Code: 13 Error Subcode : 0 Invalid SubscriberID";
236 addRemoveValidateGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet,
237 gatePath, expectedMsgStart);
241 public void testAddInvalidScnUpGate() throws Exception {
242 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
243 + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name";
244 addRemoveValidateGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet,
245 gatePath, expectedMsgStart);
249 public void testAddInvalidScnDownGate() throws Exception {
250 final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath
251 + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name";
252 addRemoveValidateGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet,
253 gatePath, expectedMsgStart);
257 * This tests the instantiation of a COPSDecisionMsg object that is responsible for setting a gate request,
258 * streams it over a mock Socket object and parses the bytes into a new COPSDecisionMsg object which should
260 * @throws Exception - test will fail should any exception be thrown during execution
263 public void testGateRequestDecisionMsg() throws Exception {
264 final Socket socket = new MockSocket();
266 final ServiceFlowDirection direction = ServiceFlowDirection.Us;
267 final Gates gate = makeGateObj("extrm_up", cmtsAddr, direction, new Ipv4Address("127.0.0.1"));
268 final IPCMMGate gateReq = makeGateRequest(ccap, gate, InetAddress.getByName("localhost"), direction);
269 final byte[] data = gateReq.getData();
271 final Set<COPSDecision> decisionSet = new HashSet<>();
272 decisionSet.add(new COPSDecision(CType.DEF, Command.INSTALL, DecisionFlag.REQERROR));
273 final Map<COPSContext, Set<COPSDecision>> decisionMap = new HashMap<>();
274 decisionMap.put(new COPSContext(RType.CONFIG, (short) 0), decisionSet);
276 final COPSClientSI clientSD = new COPSClientSI(CNum.DEC, CType.CSI, new COPSData(data, 0, data.length));
277 final COPSDecisionMsg decisionMsg = new COPSDecisionMsg(IPCMMClient.CLIENT_TYPE, new COPSHandle(new COPSData("123")),
278 decisionMap, null, clientSD);
279 decisionMsg.writeData(socket);
281 final COPSMsg msg = COPSMsgParser.parseMessage(socket);
282 Assert.assertNotNull(msg);
283 Assert.assertEquals(decisionMsg, msg);
287 * Attempts to create a gate against a CMTS, validates the results then attempts to delete it.
288 * @param service - the service used to connect to a CMTS for issuing requests
289 * @param scnName - the service class name (aka. gate name)
290 * @param srcAddr - the address to the CMTS subnet?
291 * @param dstAddr - the destination address
292 * @param direction - the gate direction
293 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
294 * @param gatePath - the path to the gate
295 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
297 private void addRemoveValidateGate(final PCMMService service, final String scnName, final Ipv4Address srcAddr,
298 final Ipv4Address dstAddr, final ServiceFlowDirection direction,
299 final InetAddress cmAddrInet, final String gatePath,
300 final String expGateSetMsgStart) {
301 connectToCmts(service);
302 addAndValidateGate(service, scnName, srcAddr, dstAddr, direction, cmAddrInet, gatePath, expGateSetMsgStart);
303 deleteGate(service, gatePath);
306 private void connectToCmts(final PCMMService service) {
307 final String message = service.addCcap();
308 Assert.assertNotNull(message);
309 final String expectedMsg = "200 OK - CCAP " + ccapId + " connected @ "
310 + ccap.getConnection().getIpAddress().getIpv4Address().getValue()
311 + ":" + ccap.getConnection().getPort().getValue();
312 Assert.assertEquals(expectedMsg, message);
313 Assert.assertNotNull(service.ccapClient.pcmmPdp.getClientHandle());
317 * Attempts to create a gate against a CMTS and validates the results.
318 * @param service - the service used to connect to a CMTS for issuing requests
319 * @param scnName - the service class name (aka. gate name)
320 * @param srcAddr - the address to the CMTS subnet?
321 * @param dstAddr - the destination address
322 * @param direction - the gate direction
323 * @param cmAddrInet - the address to the cable modem to which the gate will be assigned
324 * @param gatePath - the path to the gate
325 * @param expGateSetMsgStart - the expected start of the gate set return message to be validated against
327 private void addAndValidateGate(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 final Gates gate = makeGateObj(scnName, srcAddr, direction, dstAddr);
333 final String gateSetMsg = service.sendGateSet(gatePath, cmAddrInet, gate, direction);
334 Assert.assertNotNull(gateSetMsg);
335 Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart));
337 // TODO - add validation to the PCMMGateReq contained within the map
338 Assert.assertNotNull(service.gateRequests.get(gatePath));
342 * Attempts to delete a gate
343 * @param service - the service used to connect to a CMTS for issuing requests
344 * @param gatePath - the path to the gate
346 private boolean deleteGate(final PCMMService service, final String gatePath) {
347 final boolean out = service.sendGateDelete(gatePath);
349 // Wait up to 1 sec for response to be processed
350 final long start = System.currentTimeMillis();
351 while (1000 < System.currentTimeMillis() - start) {
352 if (service.gateRequests.size() == 0) break;
354 Assert.assertNull(service.gateRequests.get(gatePath));
359 * Creates a mock Ccaps object that can be used for connecting to a CMTS
360 * @param inPort - the CMTS port number
361 * @param ipAddr - the CMTS IPv4 address string
362 * @param ccapId - the ID of the CCAP
363 * @return - the mock Ccaps object
365 private Ccaps makeCcapsObj(final int inPort, final Ipv4Address ipAddr, final String ccapId) {
366 final Ccaps ccap = Mockito.mock(Ccaps.class);
367 final Connection conn = Mockito.mock(Connection.class);
368 Mockito.when(ccap.getConnection()).thenReturn(conn);
369 final PortNumber port = Mockito.mock(PortNumber.class);
370 Mockito.when(conn.getPort()).thenReturn(port);
371 Mockito.when(port.getValue()).thenReturn(inPort);
373 final IpAddress addr = Mockito.mock(IpAddress.class);
374 Mockito.when(conn.getIpAddress()).thenReturn(addr);
375 Mockito.when(addr.getIpv4Address()).thenReturn(ipAddr);
377 Mockito.when(ccap.getCcapId()).thenReturn(ccapId);
378 final AmId amid = Mockito.mock(AmId.class);
379 Mockito.when(ccap.getAmId()).thenReturn(amid);
380 Mockito.when(amid.getAmTag()).thenReturn(0xcada);
381 Mockito.when(amid.getAmType()).thenReturn(1);
387 * Creates a mock Gates object
388 * @param scnValue - the service class name defined on the CMTS
389 * @param dstAddr - the CM address this gate should be set against
390 * @return - the gate request
392 private Gates makeGateObj(final String scnValue, final Ipv4Address srcAddr, final ServiceFlowDirection direction,
393 final Ipv4Address dstAddr) {
394 final Gates gate = Mockito.mock(Gates.class);
395 final GateSpec gateSpec = Mockito.mock(GateSpec.class);
396 Mockito.when(gate.getGateSpec()).thenReturn(gateSpec);
397 Mockito.when(gateSpec.getDirection()).thenReturn(direction);
398 // TODO - make sure to write a test when this value is not null
399 Mockito.when(gateSpec.getDscpTosOverwrite()).thenReturn(null);
400 final TrafficProfile trafficProfile = Mockito.mock(TrafficProfile.class);
401 final ServiceClassName scn = Mockito.mock(ServiceClassName.class);
402 Mockito.when(scn.getValue()).thenReturn(scnValue);
403 Mockito.when(trafficProfile.getServiceClassName()).thenReturn(scn);
404 Mockito.when(gate.getTrafficProfile()).thenReturn(trafficProfile);
406 // TODO - write tests when this is null and ExtClassifier or Ipv6Classifier objects are not null
407 final Classifier classifier = Mockito.mock(Classifier.class);
409 // This is the address of the CM
410 Mockito.when(classifier.getDstIp()).thenReturn(dstAddr);
412 final PortNumber dstPort = new PortNumber(4321);
413 Mockito.when(classifier.getDstPort()).thenReturn(dstPort);
414 final TpProtocol protocol = new TpProtocol(0);
415 Mockito.when(classifier.getProtocol()).thenReturn(protocol);
416 Mockito.when(classifier.getSrcIp()).thenReturn(srcAddr);
417 final PortNumber srcPort = new PortNumber(1234);
418 Mockito.when(classifier.getSrcPort()).thenReturn(srcPort);
420 // TODO - Can this value be any other value than 0 or 1 (See TosByte enumeration)
421 final TosByte tosByte = new TosByte((short)0);
422 Mockito.when(classifier.getTosByte()).thenReturn(tosByte);
423 final TosByte tosMask = new TosByte((short)224);
424 Mockito.when(classifier.getTosMask()).thenReturn(tosMask);
426 // TODO - enhance to test support of the other classifier types
427 Mockito.when(gate.getClassifier()).thenReturn(classifier);
428 Mockito.when(gate.getExtClassifier()).thenReturn(null);
429 Mockito.when(gate.getIpv6Classifier()).thenReturn(null);
433 private IPCMMGate makeGateRequest(final Ccaps ccap, final Gates gateReq, final InetAddress addrSubId,
434 final ServiceFlowDirection direction) {
435 final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
436 gateBuilder.build(ccap.getAmId());
437 gateBuilder.build(addrSubId);
438 // force gateSpec.Direction to align with SCN direction
439 final ServiceClassName scn = gateReq.getTrafficProfile().getServiceClassName();
441 gateBuilder.build(gateReq.getGateSpec(), direction);
444 gateBuilder.build(gateReq.getGateSpec(), null);
446 gateBuilder.build(gateReq.getTrafficProfile());
448 // pick a classifier type (only one for now)
449 if (gateReq.getClassifier() != null) {
450 gateBuilder.build(gateReq.getClassifier());
451 } else if (gateReq.getExtClassifier() != null) {
452 gateBuilder.build(gateReq.getExtClassifier());
453 } else if (gateReq.getIpv6Classifier() != null) {
454 gateBuilder.build(gateReq.getIpv6Classifier());
456 // assemble the final gate request
457 return gateBuilder.getGateReq();
460 private class MockSocket extends Socket {
462 private ByteArrayOutputStream os = new ByteArrayOutputStream();
463 private ByteArrayInputStream is;
466 public OutputStream getOutputStream() {
471 public InputStream getInputStream() {
472 if (is == null) is = new ByteArrayInputStream(os.toByteArray());