From: Ryan Vail Date: Tue, 1 Dec 2015 17:16:16 +0000 (-0600) Subject: Added GateInfo to Op Ds and RPCs to support gate update requests X-Git-Tag: release/beryllium~2 X-Git-Url: https://git.opendaylight.org/gerrit/gitweb?a=commitdiff_plain;h=896fa34dd5d1779138a10e253a2d21cbac2f1780;p=packetcable.git Added GateInfo to Op Ds and RPCs to support gate update requests Extended driver with GateState and GateInfo types. Added GateInfo data to operationa datastore. Added RPC to request GateState updating. Updated cmts emulator to respond to gate info requests Change-Id: I35bccb12636359c3613b4b06958a348037cef93b Signed-off-by: Ryan Vail --- diff --git a/packetcable-driver/src/main/java/org/pcmm/PCMMPdpMsgSender.java b/packetcable-driver/src/main/java/org/pcmm/PCMMPdpMsgSender.java index 91303b1..d156064 100644 --- a/packetcable-driver/src/main/java/org/pcmm/PCMMPdpMsgSender.java +++ b/packetcable-driver/src/main/java/org/pcmm/PCMMPdpMsgSender.java @@ -98,7 +98,7 @@ public class PCMMPdpMsgSender extends COPSMsgSender { final ITransactionID trID = new TransactionID(_transactionID, GateCommandType.GATE_SET); gate.setTransactionID(trID); - // retain the transactionId to gate request mapping for gateID recovery after response + // retain the transactitrIDnumonId to gate request mapping for gateID recovery after response // see PCMMPdpReqStateMan.processReport() final Short trIDnum = trID.getTransactionIdentifier(); logger.info("Adding gate to cache - " + gate + " with key - " + trIDnum); @@ -227,13 +227,43 @@ public class PCMMPdpMsgSender extends COPSMsgSender { * * @throws COPSPdpException */ - public void sendGateInfo() throws COPSPdpException { + public void sendGateInfo(final IPCMMGate gate) throws COPSPdpException { /* * ::= [] [] */ - final COPSSyncStateMsg msg = new COPSSyncStateMsg(getClientType(), _handle, null); + + // added + final ITransactionID trID = new TransactionID(_transactionID, GateCommandType.GATE_INFO); + gate.setTransactionID(trID); + // retain the transactionId to gate request mapping for gateID recovery after response + // see PCMMPdpReqStateMan.processReport() + final Short trIDnum = trID.getTransactionIdentifier(); + logger.info("Adding gate to cache - " + gate + " with key - " + trIDnum); + PCMMGlobalConfig.transactionGateMap.put(trIDnum, gate); + + // gateDelete only requires AMID, subscriberID, and gateID + // remove the gateSpec, traffic profile, and classifiers from original gate request + gate.setGateSpec(null); + gate.setTrafficProfile(null); + gate.setClassifiers(null); + // clear the error object + gate.setError(null); + + + // XXX - GateID + final byte[] data = gate.getData(); + final Set decisionSet = new HashSet<>(); + decisionSet.add(new COPSDecision(CType.DEF, Command.INSTALL, DecisionFlag.REQERROR)); + final Map> decisionMap = new HashMap<>(); + decisionMap.put(new COPSContext(RType.CONFIG, (short)0), decisionSet); + final COPSClientSI clientSD = new COPSClientSI(CNum.DEC, CType.CSI, new COPSData(data, 0, data.length)); + + //final COPSSyncStateMsg msg = new COPSSyncStateMsg(getClientType(), _handle, null); + final COPSDecisionMsg decisionMsg = new COPSDecisionMsg(getClientType(), _handle, decisionMap, null, clientSD); + try { - msg.writeData(_sock); + //msg.writeData(_sock); + decisionMsg.writeData(_sock); } catch (IOException e) { throw new COPSPdpException("Failed to send the GateInfo request", e); } diff --git a/packetcable-driver/src/main/java/org/pcmm/PCMMPdpReqStateMan.java b/packetcable-driver/src/main/java/org/pcmm/PCMMPdpReqStateMan.java index b80bf68..9c7ee19 100644 --- a/packetcable-driver/src/main/java/org/pcmm/PCMMPdpReqStateMan.java +++ b/packetcable-driver/src/main/java/org/pcmm/PCMMPdpReqStateMan.java @@ -9,6 +9,9 @@ package org.pcmm; import org.pcmm.gates.IGateID; +import org.pcmm.gates.IGateState; +import org.pcmm.gates.IGateTimeInfo; +import org.pcmm.gates.IGateUsageInfo; import org.pcmm.gates.IPCMMError.ErrorCode; import org.pcmm.gates.IPCMMGate; import org.pcmm.gates.ITransactionID; @@ -125,8 +128,17 @@ public class PCMMPdpReqStateMan extends COPSPdpReqStateMan { logger.info("rtypemsg success"); _status = Status.ST_REPORT; final IGateID gateID = gateMsg.getGateID(); - logger.info("Setting gate ID on gate object - " + gateID); + //logger.info("Setting gate ID on gate object - " + gateID); gate.setGateID(gateID); + + //setting the Gate State, Time Info and Usage Info + final IGateState igateState = gateMsg.getGateState(); + gate.setGateState(igateState); + final IGateTimeInfo gateTimeInfo = gateMsg.getGateTimeInfo(); + gate.setGateTimeInfo(gateTimeInfo); + final IGateUsageInfo gateUsageInfo = gateMsg.getGateUsageInfo(); + gate.setGateUsageInfo(gateUsageInfo); + if (_thisProcess != null) _thisProcess.successReport(this, gateMsg); } else { @@ -135,12 +147,17 @@ public class PCMMPdpReqStateMan extends COPSPdpReqStateMan { cmdType = "GateDeleteAck"; } else if (trID.getGateCommandType().equals(GateCommandType.GATE_SET_ACK)) { cmdType = "GateSetAck"; + } else if (trID.getGateCommandType().equals(GateCommandType.GATE_INFO_ACK)) { + cmdType = "GateInfoAck"; } else cmdType = null; // capture the gateId from the response message final IGateID gateID = gateMsg.getGateID(); logger.info("Setting gate ID on gate object - " + gateID); gate.setGateID(gateID); - + // capture the gate state from the response message + final IGateState igateState = gateMsg.getGateState(); + logger.info("Setting gate ID on gate object - " + gateID); + gate.setGateState(igateState); if (gateID != null) { int gateIdInt = gateID.getGateID(); String gateIdHex = String.format("%08x", gateIdInt); diff --git a/packetcable-driver/src/main/java/org/pcmm/base/impl/PCMMBaseObject.java b/packetcable-driver/src/main/java/org/pcmm/base/impl/PCMMBaseObject.java index 3aaf34c..cebd2c4 100644 --- a/packetcable-driver/src/main/java/org/pcmm/base/impl/PCMMBaseObject.java +++ b/packetcable-driver/src/main/java/org/pcmm/base/impl/PCMMBaseObject.java @@ -123,7 +123,7 @@ public abstract class PCMMBaseObject implements IPCMMBaseObject { USER_ID((byte) 20), SHARED_RES_ID((byte) 21); - private byte value; + private final byte value; SNum(byte value) { this.value = value; diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/IGateSpec.java b/packetcable-driver/src/main/java/org/pcmm/gates/IGateSpec.java index f54303d..b24ff4f 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/IGateSpec.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/IGateSpec.java @@ -20,8 +20,8 @@ import org.pcmm.base.IPCMMBaseObject; * * * SessionClassID * * Direction - * * Authorized Timer - * * Reserved Timer + * * AUTHORIZED Timer + * * RESERVED Timer * * Committed Timer * * Committed Recovery Timer * * DSCP/TOS Overwrite @@ -36,10 +36,10 @@ import org.pcmm.base.IPCMMBaseObject; * MUST reserve and activate the DOCSIS flows accordingly. For Multicast Gates the CMTS needs to only support * flows or gates in the downstream direction. * - * Authorized Timer limits the amount of time the authorization must remain valid before it is reserved (see + * AUTHORIZED Timer limits the amount of time the authorization must remain valid before it is reserved (see * Section 6.2). * - * Reserved Timer limits the amount of time the reservation must remain valid before the resources are committed (see + * RESERVED Timer limits the amount of time the reservation must remain valid before the resources are committed (see * Section 6.2). * * Committed Timer limits the amount of time a committed service flow may remain idle. @@ -129,7 +129,7 @@ public interface IGateSpec extends IPCMMBaseObject { Direction getDirection(); /** - * Authorized Timer limits the amount of time the authorization must remain + * AUTHORIZED Timer limits the amount of time the authorization must remain * valid before it is reserved * * @return time in ms; @@ -137,7 +137,7 @@ public interface IGateSpec extends IPCMMBaseObject { short getTimerT1(); /** - * Reserved Timer limits the amount of time the reservation must remain + * RESERVED Timer limits the amount of time the reservation must remain * valid before the resources are committed * * @return time in ms; diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/IGateState.java b/packetcable-driver/src/main/java/org/pcmm/gates/IGateState.java new file mode 100644 index 0000000..33db544 --- /dev/null +++ b/packetcable-driver/src/main/java/org/pcmm/gates/IGateState.java @@ -0,0 +1,153 @@ +/* + * Copyright (c) 2015 CableLabs and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.pcmm.gates; + +import com.google.common.base.CaseFormat; +import com.google.common.base.Converter; +import org.pcmm.base.IPCMMBaseObject; + +/** + * From the Packetcable Multimedia specification section 6.4.2.15 + *

+ * The information in the Gate State object reflects the current state of the Gate. The CMTS MUST include the Gate + * State object in any unsolicited messages that it sends to the Policy Server. The Policy Server may use this + * information to report state to the Application Manager, or for enforcing complex rules that might require state + * knowledge of the Gate. + */ +public interface IGateState extends IPCMMBaseObject { + + static final Converter converter = CaseFormat.UPPER_UNDERSCORE.converterTo(CaseFormat.UPPER_CAMEL); + /** + * The S-Type for Gate State + */ + byte STYPE = 1; + + /** + * Returns the gate State value + * + * @return - the State (2 bytes unsigned integer) + */ + //short getGateState(); + GateStateType getGateState(); + + GateStateReasonType getGateStateReason(); + + /** + * Gate State Types types + */ + enum GateStateType { + + IDLE_CLOSED((short) 1), + AUTHORIZED((short) 2), + RESERVED((short) 3), + COMMITTED((short) 4), + COMMITTED_RECOVERY((short) 5); + + GateStateType(short value) { + this.value = value; + this.displayName = converter.convert(name()); + } + + public short getValue() { + return value; + } + + public static GateStateType valueOf(short v) { + switch (v) { + case 1: + return GateStateType.IDLE_CLOSED; + case 2: + return GateStateType.AUTHORIZED; + case 3: + return GateStateType.RESERVED; + case 4: + return GateStateType.COMMITTED; + case 5: + return GateStateType.COMMITTED_RECOVERY; + default: + throw new IllegalArgumentException("not supported value"); + } + } + + private final short value; + private final String displayName; + + public String toString() { + return displayName + "(" + getValue() + ")"; + } + } + + + /** + * From PCMM Spec section 6.4.2.15 + * Reason is a 2-byte unsigned integer field which MUST indicate one of the following reasons for this update:

+     * 1 = Close initiated by CMTS due to reservation reassignment
+     * 2 = Close initiated by CMTS due to lack of DOCSIS MAC-layer responses
+     * 3 = Close initiated by CMTS due to timer T1 expiration
+     * 4 = Close initiated by CMTS due to timer T2 expiration
+     * 5 = Inactivity timer expired due to Service Flow inactivity (timer T3 expiration)
+     * 6 = Close initiated by CMTS due to lack of Reservation Maintenance
+     * 7 = Gate state unchanged, but volume limit reached
+     * 8 = Close initiated by CMTS due to timer T4 expiration
+     * 9 = Gate state unchanged, but timer T2 expiration caused reservation reduction
+     * 10 = Gate state unchanged, but time limit reached
+     * 11 = Close initiated by Policy Server or CMTS, volume limit reached
+     * 12 = Close initiated by Policy Server or CMTS, time limit reached
+     * 13 = Close initiated by CMTS, other
+     * 14 = Gate state unchanged, but SharedResourceID updated
+     * 15 = Close initiated by CMTS due to loss of shared resource
+     * 65535 = Other
+     * 
+ */ + enum GateStateReasonType { + ZERO((short)0), + RESERVATION_REASSIGNMENT((short) 1), + LACK_OF_DOCSIS_MAC_LAYER_RESPONSES((short) 2), + T1_EXPIRATION((short)3), + T2_EXPIRATION((short)4), + T3_EXPIRATION((short)5), + LACK_OF_RESERVATION_MAINTENANCE((short)6), + UNCHANGED_BUT_VOLUME_LIMIT_REACHED((short)7), + T4_EXPIRATION((short)8), + UNCHANGED_BUT_T2_EXPIRATION_CAUSED_RESERVATION_REDUCTION((short)9), + UNCHANGED_BUT_TIME_LIMIT_REACHED((short)10), + VOLUME_LIMIT_REACHED((short)11), + TIME_LIMIT_REACHED((short)12), + CMTS_OTHER((short)13), + UNCHANGED_BUT_SHARED_RESOURCE_ID_CHANGED((short)14), + LOSS_OF_SHARED_RESOURCE((short)15), + OTHER((short) 65535); + + GateStateReasonType(short value) { + this.value = value; + this.displayName = converter.convert(name()); + } + + public short getValue() { + return value; + } + + public static GateStateReasonType valueOf(short v) { + for (GateStateReasonType type : GateStateReasonType.values()) { + if (type.getValue() == v) { + return type; + } + } + throw new IllegalArgumentException("not supported value: " + v); + } + + private final short value; + private final String displayName; + + public String toString() { + return displayName + "(" + getValue() + ")"; + } + } + +} diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/IGateTimeInfo.java b/packetcable-driver/src/main/java/org/pcmm/gates/IGateTimeInfo.java new file mode 100644 index 0000000..75f8ab3 --- /dev/null +++ b/packetcable-driver/src/main/java/org/pcmm/gates/IGateTimeInfo.java @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2015 CableLabs and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.pcmm.gates; + +import org.pcmm.base.IPCMMBaseObject; + +/** + * + * From the Packetcable Multimedia specification section 6.4.2.12 + * + *The Gate Time Info object contains the total amount of time the Gate has been in the Committed and + * Committed Recovery states. This counter MUST be stopped upon the Gate transitioning out of the + * Committed or Committed Recovery states to either the RESERVED state or AUTHORIZED state. If the + * Gate subsequently transitions back to the Committed state, this counter MUST be restarted where + * it last stopped, i.e., when transitioning out of the Committed or Committed Recovery states. + */ +public interface IGateTimeInfo extends IPCMMBaseObject { + + /** + * The S-Type for Gate Time Info + */ + byte STYPE = 1; + + + /** + * Time Committed total amount of time the Gate has been in the Committed and Committed + * Recovery states + * + * @return time in seconds; + */ + int getGateTimeInfo(); +} diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/IGateUsageInfo.java b/packetcable-driver/src/main/java/org/pcmm/gates/IGateUsageInfo.java new file mode 100644 index 0000000..dddf88e --- /dev/null +++ b/packetcable-driver/src/main/java/org/pcmm/gates/IGateUsageInfo.java @@ -0,0 +1,35 @@ +/* + * Copyright (c) 2015 CableLabs and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.pcmm.gates; + +import org.pcmm.base.IPCMMBaseObject; + +/** + * + * From the Packetcable Multimedia specification section 6.4.2.13 + * + * The Gate Usage Info object contains a counter indicating the number of kilobytes transmitted + * over this Gate. + */ +public interface IGateUsageInfo extends IPCMMBaseObject { + + /** + * The S-Type for Gate Time Info + */ + byte STYPE = 1; + + + /** + * Time Committed total amount of time the Gate has been in the Committed and Committed + * Recovery states + * + * @return usage in kbps; + */ + long getGateUsageInfo(); +} \ No newline at end of file diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/IPCMMError.java b/packetcable-driver/src/main/java/org/pcmm/gates/IPCMMError.java index 75af85b..001817d 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/IPCMMError.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/IPCMMError.java @@ -26,9 +26,9 @@ public interface IPCMMError extends IPCMMBaseObject { /** * Returns the error sub-code - * @return - not null (mostly will be NA) + * @return - the sub error code */ - ErrorCode getErrorSubcode(); + short getErrorSubcode(); /** * Returns the error's description diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/IPCMMGate.java b/packetcable-driver/src/main/java/org/pcmm/gates/IPCMMGate.java index 5f8cbe1..99acee0 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/IPCMMGate.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/IPCMMGate.java @@ -47,6 +47,12 @@ public interface IPCMMGate { */ void setGateID(IGateID gateid); + /** + * gateState is the handle for the Gate State. + * + */ + void setGateState(IGateState gateState); + /** * (i.e., QoS limits, timers, etc.). * @@ -69,6 +75,10 @@ public interface IPCMMGate { void setTransactionID(ITransactionID transactionID); void setError(IPCMMError error); + + void setGateTimeInfo(IGateTimeInfo gateTimeInfo); + + void setGateUsageInfo(IGateUsageInfo gateUsageInfo); ITransactionID getTransactionID(); @@ -79,6 +89,14 @@ public interface IPCMMGate { */ IGateID getGateID(); + /** + * GateState is the handle for the GateState. + * + * @return gateSTATE + */ + IGateState getGateState(); + + /** * AMID is the handle that identifies the Application Manager and * Application Type @@ -121,6 +139,9 @@ public interface IPCMMGate { */ IPCMMError getError(); + IGateTimeInfo getGateTimeInfo(); + + IGateUsageInfo getGateUsageInfo(); /** * diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/ITrafficProfile.java b/packetcable-driver/src/main/java/org/pcmm/gates/ITrafficProfile.java index 80f6ea3..f887240 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/ITrafficProfile.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/ITrafficProfile.java @@ -12,7 +12,7 @@ import org.pcmm.base.IPCMMBaseObject; public interface ITrafficProfile extends IPCMMBaseObject { - // Authorized + // AUTHORIZED byte DEFAULT_ENVELOP = 0x7; byte getEnvelop(); diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/impl/AMID.java b/packetcable-driver/src/main/java/org/pcmm/gates/impl/AMID.java index f2124c9..cfcab5e 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/impl/AMID.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/impl/AMID.java @@ -85,6 +85,14 @@ public class AMID extends PCMMBaseObject implements IAMID { return result; } + @Override + public String toString() { + return "AMID{" + + "appType=" + appType + + ", appMgrTag=" + appMgrTag + + '}'; + } + /** * Returns an AMID object from a byte array * @param data - the data to parse diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/impl/BEEnvelop.java b/packetcable-driver/src/main/java/org/pcmm/gates/impl/BEEnvelop.java index 220e92a..361ba1f 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/impl/BEEnvelop.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/impl/BEEnvelop.java @@ -52,17 +52,17 @@ public class BEEnvelop { private final int maxTrafficBurst; /** - * Minimum Reserved Traffic Rate is a 4-byte unsigned integer field specifying the minimum rate, in bits/sec, + * Minimum RESERVED Traffic Rate is a 4-byte unsigned integer field specifying the minimum rate, in bits/sec, * reserved for this Service Flow. This field is fully defined in section C.2.2.5.4. A default Mini Rate of 0 SHOULD - * be used if a specific Minimum Reserved Traffic Rate is not required. + * be used if a specific Minimum RESERVED Traffic Rate is not required. */ private final int minResTrafficRate; /** - * Assumed Minimum Reserved Traffic Rate Packet Size is a 2-byte unsigned integer field specifying an assumed - * minimum packet size, in bytes, for which the Minimum Reserved Traffic Rate will be provided for this flow. This - * field is fully defined in section C.2.2.5.5. A default Assumed Minimum Reserved Traffic Rate Packet Size of - * 0 SHOULD be used if a specific Assumed Minimum Reserved Traffic Rate Packet size is not required. Upon receip + * Assumed Minimum RESERVED Traffic Rate Packet Size is a 2-byte unsigned integer field specifying an assumed + * minimum packet size, in bytes, for which the Minimum RESERVED Traffic Rate will be provided for this flow. This + * field is fully defined in section C.2.2.5.5. A default Assumed Minimum RESERVED Traffic Rate Packet Size of + * 0 SHOULD be used if a specific Assumed Minimum RESERVED Traffic Rate Packet size is not required. Upon receip * of a value of 0 the CMTS MUST utilize its implementation-specific default size for this parameter, not 0 bytes. */ private final short assumedMinConcatBurst; @@ -144,8 +144,8 @@ public class BEEnvelop { * @param transPolicy - the Requested Transmission Policy * @param maxSusTrafficRate - the Maximum Sustained Traffic Rate * @param maxTrafficBurst - the Maximum Traffic Burst Rate - * @param minResTrafficRate - the Minimum Reserved Traffic Rate - * @param assumedMinConcatBurst - the Assumed Minimum Reserved Traffic Rate Packet Size + * @param minResTrafficRate - the Minimum RESERVED Traffic Rate + * @param assumedMinConcatBurst - the Assumed Minimum RESERVED Traffic Rate Packet Size * @param maxConcatBurst - the Maximum Concatenated Burst * @param upPeakTrafficRate - the Upstream Peak Traffic Rate * @param reqAttrMask - the Required Attribute Mask diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/impl/BestEffortService.java b/packetcable-driver/src/main/java/org/pcmm/gates/impl/BestEffortService.java index 2b9b55b..cc1c706 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/impl/BestEffortService.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/impl/BestEffortService.java @@ -69,7 +69,7 @@ public class BestEffortService extends PCMMBaseObject implements ITrafficProfile protected BestEffortService(final byte envelope, final BEEnvelop auth, final BEEnvelop reserved, final BEEnvelop committed) { super(SNum.TRAFFIC_PROFILE, STYPE); - if (auth == null) throw new IllegalArgumentException("Authorized envelope must not be null"); + if (auth == null) throw new IllegalArgumentException("AUTHORIZED envelope must not be null"); // TODO - Cannot figure out any other means to parse the bytes unless this is true. Determine if correct??? if (reserved == null && committed != null) diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateState.java b/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateState.java new file mode 100644 index 0000000..92006ed --- /dev/null +++ b/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateState.java @@ -0,0 +1,92 @@ +/* + * Copyright (c) 2015 CableLabs and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.pcmm.gates.impl; + +import com.google.common.base.Objects; +import org.pcmm.base.impl.PCMMBaseObject; +import org.pcmm.gates.IGateState; +import org.umu.cops.stack.COPSMsgParser; + +/** + * Implementation of the IGateID interface + */ +public class GateState extends PCMMBaseObject implements IGateState { + + /** + * The Gate State value (unsigned 32 bit integer) + */ + final GateStateType gateState; + + /** + * The Gate State value (unsigned 32 bit integer) + */ + final GateStateReasonType gateStateReason; + + /** + * Constructor + */ + public GateState(final GateStateType gateState, final GateStateReasonType gateStateReason) { + super(SNum.GATE_STATE, STYPE); + this.gateState = gateState; + this.gateStateReason = gateStateReason; + } + + @Override + public GateStateType getGateState() { + return gateState; + } + + @Override + public GateStateReasonType getGateStateReason() { + return gateStateReason; + } + + @Override + protected byte[] getBytes() { + return COPSMsgParser.shortToBytes(gateState.getValue()); + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (!(o instanceof GateID)) { + return false; + } + if (!super.equals(o)) { + return false; + } + final GateState gateSTATE = (GateState) o; + return gateState == gateSTATE.gateState; + } + + @Override + public int hashCode() { + return Objects.hashCode(super.hashCode(), gateState, gateStateReason); + } + + @Override + public String toString() { + return String.format("%s/%s", gateState.toString(), gateStateReason.toString()); + } + + /** + * Returns a GateState object from a byte array + * @param data - the data to parse + * @return - the object + * TODO - make me more robust as RuntimeExceptions can be thrown here. + */ + public static GateState parse(final byte[] data) { + return new GateState(GateStateType.valueOf(COPSMsgParser.bytesToShort(data[0], data[1])), + GateStateReasonType.valueOf(COPSMsgParser.bytesToShort(data[2], data[3]))); + } + + +} diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateTimeInfo.java b/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateTimeInfo.java new file mode 100644 index 0000000..e48a7ce --- /dev/null +++ b/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateTimeInfo.java @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2015 CableLabs and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.pcmm.gates.impl; + +import org.pcmm.base.impl.PCMMBaseObject; +import org.pcmm.gates.IGateTimeInfo; +import org.umu.cops.stack.COPSMsgParser; + +/** + * Implementation of the IGateSpec interface + */ +public class GateTimeInfo extends PCMMBaseObject implements IGateTimeInfo { + + final int gateTimeInfo; + + public GateTimeInfo(final int gateTimeInfo) { + super(SNum.GATE_TIME_INFO, STYPE); + this.gateTimeInfo = gateTimeInfo; + } + + @Override + public int getGateTimeInfo() { + return gateTimeInfo; + } + + + @Override + protected byte[] getBytes() { + return COPSMsgParser.intToBytes(gateTimeInfo); + } + + public static GateTimeInfo parse(final byte[] data) { + return new GateTimeInfo((COPSMsgParser.bytesToInt(data[0], data[1],data[2], data[3]))); + } +} diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateUsageInfo.java b/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateUsageInfo.java new file mode 100644 index 0000000..39c2a71 --- /dev/null +++ b/packetcable-driver/src/main/java/org/pcmm/gates/impl/GateUsageInfo.java @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2015 CableLabs and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.pcmm.gates.impl; + +import org.pcmm.base.impl.PCMMBaseObject; +import org.pcmm.gates.IGateUsageInfo; +import org.umu.cops.stack.COPSMsgParser; + +/** + * Implementation of the IGateSpec interface + */ +public class GateUsageInfo extends PCMMBaseObject implements IGateUsageInfo { + + final long gateUsageInfo; + + public GateUsageInfo(final long gateUsageInfo) { + + super(SNum.GATE_USAGE_INFO, STYPE); + this.gateUsageInfo = gateUsageInfo; + } + + @Override + public long getGateUsageInfo() { + return gateUsageInfo; + } + + + + @Override + protected byte[] getBytes() { + byte[] first = COPSMsgParser.intToBytes((int)gateUsageInfo>>32); + byte[] second = COPSMsgParser.intToBytes((int)gateUsageInfo); + return new byte[first.length+second.length]; + } + + public static GateUsageInfo parse(final byte[] data) { + return new GateUsageInfo( + (long)(COPSMsgParser.bytesToInt(data[0], data[1],data[2], data[3]))<<32 + | (COPSMsgParser.bytesToInt(data[4], data[5],data[6], data[7]))& 0xFFFFFFFFL); + } +} diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/impl/PCMMError.java b/packetcable-driver/src/main/java/org/pcmm/gates/impl/PCMMError.java index 3913747..3a550b9 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/impl/PCMMError.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/impl/PCMMError.java @@ -25,14 +25,14 @@ public class PCMMError extends PCMMBaseObject implements IPCMMError { /** * The error sub-code (defaults to NA) */ - private final ErrorCode subErrCode; + private final short subErrCode; /** * Constructor without a sub-code which will then be set to NA * @param errorCode - the error code (required and not NA) */ public PCMMError(final ErrorCode errorCode) { - this(errorCode, null); + this(errorCode, (short)0); } /** @@ -40,13 +40,12 @@ public class PCMMError extends PCMMBaseObject implements IPCMMError { * @param errorCode - the error code (required and not NA) * @param subErrCode - the sub-code (defaults to NA when null) */ - public PCMMError(final ErrorCode errorCode, final ErrorCode subErrCode) { + public PCMMError(final ErrorCode errorCode, final short subErrCode) { super(SNum.PCMM_ERROR, STYPE); if (errorCode == null || errorCode.equals(ErrorCode.NA)) throw new IllegalArgumentException("ErrorCode is required and must not be NA"); this.errorCode = errorCode; - if (subErrCode == null) this.subErrCode = ErrorCode.NA; - else this.subErrCode = subErrCode; + this.subErrCode = subErrCode; } @Override @@ -55,7 +54,7 @@ public class PCMMError extends PCMMBaseObject implements IPCMMError { } @Override - public ErrorCode getErrorSubcode() { + public short getErrorSubcode() { return subErrCode; } @@ -66,8 +65,22 @@ public class PCMMError extends PCMMBaseObject implements IPCMMError { */ @Override public String getDescription() { - String hex = Integer.toHexString(subErrCode.getCode() & 0xFFFF); - return "Error Code: " + errorCode.getCode() + " Error Subcode : " + hex + " " + errorCode.getDescription(); + //Error-Subcode is a 2-byte unsigned integer field used to provide further information about the error. + // In the case of Error-Codes 6, 7 and 17, this 16-bit field MUST contain, as two 8-bit values the + // S-Num and S-Type of the object that is missing or in error. + String subcode; + switch (errorCode) { + case MISSING_REQ_OBJ: + case INVALID_OBJ: + case INVALID_FIELD: + byte[] sParts = COPSMsgParser.shortToBytes(subErrCode); + subcode = String.format("S-Num: %d, S-Type: %d", sParts[0], sParts[1]); + break; + default: + subcode = "Subcode: " + Integer.toHexString(subErrCode & 0xFFFF); + } + + return "Error Code: " + errorCode.getCode() + " " + subcode + " " + errorCode.getDescription(); } @Override @@ -78,7 +91,7 @@ public class PCMMError extends PCMMBaseObject implements IPCMMError { @Override protected byte[] getBytes() { final byte[] errorCodeBytes = COPSMsgParser.shortToBytes(errorCode.getCode()); - final byte[] subErrCodeBytes = COPSMsgParser.shortToBytes(subErrCode.getCode()); + final byte[] subErrCodeBytes = COPSMsgParser.shortToBytes(subErrCode); final byte[] data = new byte[errorCodeBytes.length + subErrCodeBytes.length]; System.arraycopy(errorCodeBytes, 0, data, 0, errorCodeBytes.length); System.arraycopy(subErrCodeBytes, 0, data, errorCodeBytes.length, subErrCodeBytes.length); @@ -104,7 +117,7 @@ public class PCMMError extends PCMMBaseObject implements IPCMMError { public int hashCode() { int result = super.hashCode(); result = 31 * result + errorCode.hashCode(); - result = 31 * result + subErrCode.hashCode(); + result = 31 * result + subErrCode; return result; } @@ -116,7 +129,7 @@ public class PCMMError extends PCMMBaseObject implements IPCMMError { */ public static PCMMError parse(final byte[] data) { return new PCMMError(ErrorCode.valueOf(COPSMsgParser.bytesToShort(data[0], data[1])), - ErrorCode.valueOf((COPSMsgParser.bytesToShort(data[2], data[3])))); + (COPSMsgParser.bytesToShort(data[2], data[3]))); } diff --git a/packetcable-driver/src/main/java/org/pcmm/gates/impl/PCMMGateReq.java b/packetcable-driver/src/main/java/org/pcmm/gates/impl/PCMMGateReq.java index eda3da0..af3fac1 100644 --- a/packetcable-driver/src/main/java/org/pcmm/gates/impl/PCMMGateReq.java +++ b/packetcable-driver/src/main/java/org/pcmm/gates/impl/PCMMGateReq.java @@ -44,6 +44,9 @@ public class PCMMGateReq implements IPCMMGate { // synchronization purposes private IGateID gateID; private IPCMMError error; + private IGateState igateState; + private IGateTimeInfo gateTimeInfo; + private IGateUsageInfo gateUsageInfo; /** * Constructor @@ -57,8 +60,9 @@ public class PCMMGateReq implements IPCMMGate { * @param error - the error */ public PCMMGateReq(IAMID iamid, ISubscriberID subscriberID, ITransactionID transactionID, - IGateSpec gateSpec, ITrafficProfile trafficProfile, List classifiers, IGateID gateID, - IPCMMError error) { + IGateSpec gateSpec, ITrafficProfile trafficProfile, List classifiers, + IGateID gateID,IPCMMError error,IGateState igateState, + IGateTimeInfo gateTimeInfo,IGateUsageInfo gateUsageInfo ) { // TODO - determine if and when this attribute should be used this.multicast = false; @@ -70,6 +74,9 @@ public class PCMMGateReq implements IPCMMGate { this.classifiers = Lists.newArrayList(classifiers); this.gateID = gateID; this.error = error; + this.igateState = igateState; + this.gateTimeInfo = gateTimeInfo; + this.gateUsageInfo = gateUsageInfo; } /** @@ -86,6 +93,10 @@ public class PCMMGateReq implements IPCMMGate { ITrafficProfile trafficProfile = null; List classifiers = Lists.newArrayListWithExpectedSize(4); PCMMError error = null; + GateState gateState = null; + GateTimeInfo gateTimeInfo = null; + GateUsageInfo gateUsageInfo = null; + short offset = 0; while (offset + 5 < data.length) { @@ -138,6 +149,20 @@ public class PCMMGateReq implements IPCMMGate { case PCMM_ERROR: error = PCMMError.parse(dataBuffer); break; + //adding GATE_STATE + case GATE_STATE: + gateState = GateState.parse(dataBuffer); + break; + //adding GATE_TIME_INFO + case GATE_TIME_INFO: + gateTimeInfo = GateTimeInfo.parse(dataBuffer); + logger.info("Gate Time Info: "+gateTimeInfo); + break; + //adding GATE_USAGE_INFO + case GATE_USAGE_INFO: + gateUsageInfo = GateUsageInfo.parse(dataBuffer); + logger.info("Gate Usage Info: "+gateUsageInfo); + break; default: logger.warn("Unhandled Object skept : S-NUM=" + sNum + " S-TYPE=" + sType + " LEN=" + len); @@ -145,7 +170,8 @@ public class PCMMGateReq implements IPCMMGate { offset += len; } - return new PCMMGateReq(amid, subscriberID, transactionID, gateSpec, trafficProfile, classifiers, gateID, error); + return new PCMMGateReq(amid, subscriberID, transactionID, gateSpec, trafficProfile, + classifiers, gateID, error, gateState, gateTimeInfo,gateUsageInfo); } @Override @@ -160,6 +186,11 @@ public class PCMMGateReq implements IPCMMGate { } + @Override + public void setGateState(IGateState gatestate) { + this.igateState = gatestate; + } + @Override public void setTransactionID(ITransactionID transactionID) { this.transactionID = transactionID; @@ -186,11 +217,28 @@ public class PCMMGateReq implements IPCMMGate { this.trafficProfile = profile; } + @Override + public void setGateTimeInfo(IGateTimeInfo gateTimeInfo) { + this.gateTimeInfo = gateTimeInfo; + } + + @Override + public void setGateUsageInfo(IGateUsageInfo gateUsageInfo) { + this.gateUsageInfo = gateUsageInfo; + } + + @Override public IGateID getGateID() { return gateID; } + @Override + public IGateState getGateState() { + return igateState; + } + + @Override public IAMID getAMID() { return iamid; @@ -228,6 +276,16 @@ public class PCMMGateReq implements IPCMMGate { return error; } + @Override + public IGateTimeInfo getGateTimeInfo() { + return gateTimeInfo; + } + + @Override + public IGateUsageInfo getGateUsageInfo() { + return gateUsageInfo; + } + public void setError(IPCMMError error) { this.error = error; } @@ -258,6 +316,15 @@ public class PCMMGateReq implements IPCMMGate { byteList.addAll(Bytes.asList(classifier.getAsBinaryArray())); } } + if (getGateState() != null) { + byteList.addAll(Bytes.asList(getGateState().getAsBinaryArray())); + } + if (getGateTimeInfo() != null) { + byteList.addAll(Bytes.asList(getGateTimeInfo().getAsBinaryArray())); + } + if (getGateUsageInfo() != null) { + byteList.addAll(Bytes.asList(getGateUsageInfo().getAsBinaryArray())); + } return Bytes.toArray(byteList); } diff --git a/packetcable-driver/src/main/java/org/pcmm/rcd/impl/AbstractPCMMServer.java b/packetcable-driver/src/main/java/org/pcmm/rcd/impl/AbstractPCMMServer.java index c6808c8..943d744 100644 --- a/packetcable-driver/src/main/java/org/pcmm/rcd/impl/AbstractPCMMServer.java +++ b/packetcable-driver/src/main/java/org/pcmm/rcd/impl/AbstractPCMMServer.java @@ -32,7 +32,7 @@ import java.util.concurrent.Executors; */ public abstract class AbstractPCMMServer implements IPCMMServer { - private final static Logger logger = LoggerFactory.getLogger(AbstractPCMMServer.class); + private static final Logger logger = LoggerFactory.getLogger(AbstractPCMMServer.class); /* * A ServerSocket to accept messages ( OPN requests) diff --git a/packetcable-driver/src/main/java/org/umu/cops/prpep/COPSPepConnection.java b/packetcable-driver/src/main/java/org/umu/cops/prpep/COPSPepConnection.java index af9645e..d312648 100644 --- a/packetcable-driver/src/main/java/org/umu/cops/prpep/COPSPepConnection.java +++ b/packetcable-driver/src/main/java/org/umu/cops/prpep/COPSPepConnection.java @@ -197,18 +197,24 @@ public class COPSPepConnection extends COPSConnection { return; } + // Check message type // TODO FIXME - Use of manager object could result in a NPE if (decision.getFlag().equals(DecisionFlag.REQSTATE)) { - if (decision.getCommand().equals(Command.REMOVE)) + if (decision.getCommand().equals(Command.REMOVE)) { // Delete Request State manager.processDeleteRequestState(dMsg); - else + } else if (decision.getCommand().equals(Command.INSTALL)) { // Open new Request State handleOpenNewRequestStateMsg(handle); - } else + } + else { + logger.error("Unknown command"); + } + } else { // Decision manager.processDecision(dMsg); + } } } } @@ -220,10 +226,11 @@ public class COPSPepConnection extends COPSConnection { */ private void handleOpenNewRequestStateMsg(final COPSHandle handle) throws COPSPepException { final COPSPepReqStateMan manager = _managerMap.get(handle); - if (manager == null) + if (manager == null) { logger.warn("Unable to find state manager with key - " + handle.getId().str()); - else + } else { manager.processOpenNewRequestState(); + } } /** @@ -236,10 +243,11 @@ public class COPSPepConnection extends COPSConnection { } final COPSPepReqStateMan manager = _managerMap.get(cMsg.getClientHandle()); - if (manager == null) + if (manager == null) { logger.warn("Unable to find state manager with key - " + cMsg.getClientHandle().getId().str()); - else + } else { manager.processSyncStateRequest(cMsg); + } } /** diff --git a/packetcable-driver/src/test/java/org/pcmm/gates/impl/PCMMErrorTest.java b/packetcable-driver/src/test/java/org/pcmm/gates/impl/PCMMErrorTest.java index 6896805..4a72fed 100644 --- a/packetcable-driver/src/test/java/org/pcmm/gates/impl/PCMMErrorTest.java +++ b/packetcable-driver/src/test/java/org/pcmm/gates/impl/PCMMErrorTest.java @@ -17,12 +17,12 @@ public class PCMMErrorTest { @Test(expected = IllegalArgumentException.class) public void nullErrorAndSubCodes() { - new PCMMError(null, null); + new PCMMError(null, (short)0); } @Test(expected = IllegalArgumentException.class) public void nullErrorCode() { - new PCMMError(null, ErrorCode.DOCSIS_1_CM); + new PCMMError(null, (short)0); } @Test(expected = IllegalArgumentException.class) @@ -32,9 +32,9 @@ public class PCMMErrorTest { @Test public void construction() { - final PCMMError error = new PCMMError(ErrorCode.TRANSPORT_ERROR, null); + final PCMMError error = new PCMMError(ErrorCode.TRANSPORT_ERROR, (short)0); Assert.assertEquals(ErrorCode.TRANSPORT_ERROR, error.getErrorCode()); - Assert.assertEquals(ErrorCode.NA, error.getErrorSubcode()); + Assert.assertEquals(ErrorCode.NA.getCode(), error.getErrorSubcode()); final byte[] dataBytes = error.getBytes(); Assert.assertEquals(4, dataBytes.length); @@ -47,7 +47,7 @@ public class PCMMErrorTest { @Test public void byteParsing() { - final PCMMError error = new PCMMError(ErrorCode.INVALID_FIELD, ErrorCode.INVALID_SUB_ID); + final PCMMError error = new PCMMError(ErrorCode.INVALID_FIELD, ErrorCode.INVALID_SUB_ID.getCode()); final PCMMError parsed = PCMMError.parse(error.getBytes()); Assert.assertEquals(error, parsed); } diff --git a/packetcable-emulator/conf/cmts.yaml b/packetcable-emulator/conf/cmts.yaml index 5d9147e..c31e06a 100644 --- a/packetcable-emulator/conf/cmts.yaml +++ b/packetcable-emulator/conf/cmts.yaml @@ -1,13 +1,14 @@ # The CMTS Emulator's communications port number port: 3918 +numberOfSupportedClassifiers: 4 -# The configured gates -gates: - - type: UPSTREAM +# The configured service class names +serviceClassNames: + - direction: UPSTREAM names: - extrm_up - foo_up - - type: DOWNSTREAM + - direction: DOWNSTREAM names: - extrm_dn - foo_dn diff --git a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CMTS.java b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CMTS.java index 4f1770a..12052fd 100644 --- a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CMTS.java +++ b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CMTS.java @@ -8,57 +8,38 @@ package org.pcmm.rcd.impl; -import com.fasterxml.jackson.annotation.JsonProperty; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; -import org.pcmm.gates.IGateSpec.Direction; -import org.pcmm.rcd.ICMTS; +import static com.google.common.base.Preconditions.checkNotNull; -import java.io.FileInputStream; +import com.google.common.collect.Maps; import java.io.IOException; import java.net.Socket; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.Map; +import org.pcmm.rcd.ICMTS; /** * Mock CMTS that can be used for testing. startServer() is called to start required threads after instantiation. */ public class CMTS extends AbstractPCMMServer implements ICMTS { + /** + * Emulator configuration + */ + private final CMTSConfig config; + /** * Receives messages from the COPS client */ private final Map handlerMap; - /** - * The configured gates - */ - private final Map> gateConfig; - - /** - * The connected CMTSs and whether or not they are up - */ - private final Map cmStatus; - /** * Constructor for having the server port automatically assigned * Call getPort() after startServer() is called to determine the port number of the server */ - public CMTS(final Map> gateConfig, final Map cmStatus) { - this(0, gateConfig, cmStatus); - } - - /** - * Constructor for starting the server to a pre-defined port number - * @param port - the port number on which to start the server. - */ - public CMTS(final int port, final Map> gateConfig, final Map cmStatus) { - super(port); - if (gateConfig == null || cmStatus == null) throw new IllegalArgumentException("Config must not be null"); - this.gateConfig = Collections.unmodifiableMap(gateConfig); - this.cmStatus = Collections.unmodifiableMap(cmStatus); - handlerMap = new ConcurrentHashMap<>(); - } + public CMTS(final CMTSConfig config) { + super(checkNotNull(config, "config must not be null").getPort()); + this.config = config; + handlerMap = Maps.newConcurrentMap(); + } @Override public void stopServer() { @@ -72,11 +53,13 @@ public class CMTS extends AbstractPCMMServer implements ICMTS { protected IPCMMClientHandler getPCMMClientHandler(final Socket socket) throws IOException { final String key = socket.getLocalAddress().getHostName() + ':' + socket.getPort(); if (handlerMap.get(key) == null) { - final IPCMMClientHandler handler = new CmtsPcmmClientHandler(socket, gateConfig, cmStatus); + final IPCMMClientHandler handler = new CmtsPcmmClientHandler(socket, config); handler.connect(); handlerMap.put(key, handler); return handler; - } else return handlerMap.get(key); + } else { + return handlerMap.get(key); + } } /** @@ -85,83 +68,13 @@ public class CMTS extends AbstractPCMMServer implements ICMTS { * @throws IOException - should the server fail to start for reasons such as port contention. */ public static void main(final String[] args) throws IOException { - final CmtsYaml config = getConfig(args[0]); - final CMTS cmts = new CMTS(config.port, config.getGates(), config.getCmStatus()); - cmts.startServer(); - } - - /** - * Returns the object that represents the YAML file - * @param uri - the location of the YAML file - * @return - the config object - * @throws IOException - when the URI does not contain the proper YAML file - */ - private static CmtsYaml getConfig(final String uri) throws IOException { - final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); - return mapper.readValue(new FileInputStream(uri), CmtsYaml.class); - } - - /** - * Class to hold configuration settings in a YAML file - */ - public static class CmtsYaml { - @JsonProperty("port") - private int port; - - @JsonProperty("gates") - private Collection gateConfigs; - - @JsonProperty("cmStatuses") - private Collection cmStatuses; - - public Map> getGates() { - final Map> out = new HashMap<>(); - - for (final GateConfigYaml gateConfig : gateConfigs) { - final Direction direction; - if (gateConfig.gateType.equalsIgnoreCase("UPSTREAM")) { - direction = Direction.UPSTREAM; - } else if (gateConfig.gateType.equalsIgnoreCase("DOWNSTREAM")) { - direction = Direction.DOWNSTREAM; - } else direction = null; + if (args.length != 1) { + throw new IllegalArgumentException("expected arguments: "); + } - if (direction != null) { - out.put(direction, gateConfig.gateNames); - } - } - return out; - } - - public Map getCmStatus() { - final Map out = new HashMap<>(); - - for (final CmStatusYaml cmStatus : cmStatuses) { - out.put(cmStatus.hostIp, cmStatus.status); - } - return out; - } - } - - /** - * Class to hold the YAML gate configuration values - */ - public static class GateConfigYaml { - @JsonProperty("type") - private String gateType; - - @JsonProperty("names") - private Set gateNames; - } - - /** - * Class to hold the YAML Cable Modem configuration values - */ - public static class CmStatusYaml { - @JsonProperty("host") - private String hostIp; - - @JsonProperty("status") - private boolean status; + final CMTSConfig config = CMTSConfig.loadConfig(args[0]); + final CMTS cmts = new CMTS(config); + cmts.startServer(); } } diff --git a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CMTSConfig.java b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CMTSConfig.java new file mode 100644 index 0000000..894df26 --- /dev/null +++ b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CMTSConfig.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015 CableLabs and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.pcmm.rcd.impl; + +import static com.google.common.base.Preconditions.checkNotNull; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.ImmutableSet; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Collections; +import java.util.Map; +import java.util.Set; +import org.pcmm.gates.IGateSpec; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +/** + * Class to hold configuration settings in a YAML file + */ +public class CMTSConfig { + + private static final Logger logger = LoggerFactory.getLogger(CMTSConfig.class); + + /** + * Returns the object that represents the YAML file + * @param uri - the location of the YAML file + * @return - the config object + * @throws IOException - when the URI does not contain the proper YAML file + */ + public static CMTSConfig loadConfig(final String uri) throws IOException { + final ObjectMapper mapper = new ObjectMapper(new YAMLFactory()); + final CmtsYmal cmtsYmal = mapper.readValue(new FileInputStream(uri), CmtsYmal.class); + + final Map> scns = cmtsYmal.getServiceClassNames(); + + final Set upstreamSCNs = scns.containsKey(IGateSpec.Direction.UPSTREAM) + ? scns.get(IGateSpec.Direction.UPSTREAM) + : Collections.emptySet(); + + final Set downstreamSCNs = scns.containsKey(IGateSpec.Direction.DOWNSTREAM) + ? scns.get(IGateSpec.Direction.DOWNSTREAM) + : Collections.emptySet(); + + if (upstreamSCNs.isEmpty() && downstreamSCNs.isEmpty()) { + logger.error("No upstream or downstream service class names defined in config"); + } + + return new CMTSConfig(cmtsYmal.getPort(), + cmtsYmal.getNumberOfSupportedClassifiers(), + upstreamSCNs, + downstreamSCNs, + cmtsYmal.getCmStatus()); + } + + private final int port; + + private final short numberOfSupportedClassifiers; + + private final ImmutableSet upstreamServiceClassNames; + + private final ImmutableSet downstreamServiceClassNames; + + private final ImmutableMap modemStatus; + + public CMTSConfig(final int port, final short numberOfSupportedClassifiers, + final Set upstreamServiceClassNames, final Set downstreamServiceClassNames, + final Map modemStatus) { + checkNotNull(upstreamServiceClassNames, "upstreamServiceClassNames must not be null"); + checkNotNull(downstreamServiceClassNames, "downstreamServiceClassNames must not be null"); + checkNotNull(modemStatus, "modemStatus must not be null"); + + this.port = port; + this.numberOfSupportedClassifiers = numberOfSupportedClassifiers; + this.upstreamServiceClassNames = ImmutableSet.copyOf(upstreamServiceClassNames); + this.downstreamServiceClassNames = ImmutableSet.copyOf(downstreamServiceClassNames); + this.modemStatus = ImmutableMap.copyOf(modemStatus); + } + + public int getPort() { + return port; + } + + public short getNumberOfSupportedClassifiers() { + return numberOfSupportedClassifiers; + } + + public ImmutableSet getUpstreamServiceClassNames() { + return upstreamServiceClassNames; + } + + public ImmutableSet getDownstreamServiceClassNames() { + return downstreamServiceClassNames; + } + + public ImmutableMap getModemStatus() { + return modemStatus; + } +} diff --git a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsDataProcessor.java b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsDataProcessor.java index b11abbf..585df41 100644 --- a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsDataProcessor.java +++ b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsDataProcessor.java @@ -58,7 +58,7 @@ class CmtsDataProcessor implements COPSPepDataProcess { final ITransactionID transactionID = PCMMGateReq.parse(new COPSData(data).getData()).getTransactionID(); // TODO - Determine how and why a response gate request can have only a transaction ID??? - final IPCMMGate responseGate = new PCMMGateReq(null, null, transactionID, null, null, null, null, null); + final IPCMMGate responseGate = new PCMMGateReq(null, null, transactionID, null, null, null, null, null, null, null, null); // TODO FIXME - Why is the key always null??? What value should be used here??? final String key = null; diff --git a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsPcmmClientHandler.java b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsPcmmClientHandler.java index f2e549a..85c8a22 100644 --- a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsPcmmClientHandler.java +++ b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsPcmmClientHandler.java @@ -8,6 +8,8 @@ package org.pcmm.rcd.impl; +import static com.google.common.base.Preconditions.checkNotNull; + import org.pcmm.gates.IGateSpec.Direction; import org.pcmm.messages.impl.MessageFactory; import org.pcmm.rcd.IPCMMServer.IPCMMClientHandler; @@ -30,7 +32,7 @@ import java.util.concurrent.Callable; */ public class CmtsPcmmClientHandler extends AbstractPCMMClient implements IPCMMClientHandler { - private final static Logger logger = LoggerFactory.getLogger(CmtsPcmmClientHandler.class); + private static final Logger logger = LoggerFactory.getLogger(CmtsPcmmClientHandler.class); /** * The thread accepting PEP COPS messages @@ -38,40 +40,29 @@ public class CmtsPcmmClientHandler extends AbstractPCMMClient implements IPCMMCl private transient Thread thread; /** - * The configured gates - */ - private final Map> gateConfig; - - /** - * The connected cable modems and whether or not they are up + * Emulator configuration */ - private final Map cmStatus; + private final CMTSConfig config; /** * Constructor when a socket connection has not been established * @param host - the host to connect * @param port - the port to connect - * @param gateConfig - the configured gates - * @param cmStatus - the configured cable modem and their state + * @param config - emulator configuration */ - public CmtsPcmmClientHandler(final String host, final int port, final Map> gateConfig, - final Map cmStatus) { + public CmtsPcmmClientHandler(final String host, final int port, final CMTSConfig config) { super(host, port); - this.gateConfig = Collections.unmodifiableMap(gateConfig); - this.cmStatus = Collections.unmodifiableMap(cmStatus); + this.config = checkNotNull(config); } /** * Constructor with a connected socket. * @param socket - the socket connection - * @param gateConfig - the configured gates - * @param cmStatus - the configured cable modem and their state + * @param config - emulator configuration */ - public CmtsPcmmClientHandler(final Socket socket, final Map> gateConfig, - final Map cmStatus) { + public CmtsPcmmClientHandler(final Socket socket, final CMTSConfig config) { super(socket); - this.gateConfig = Collections.unmodifiableMap(gateConfig); - this.cmStatus = Collections.unmodifiableMap(cmStatus); + this.config = checkNotNull(config); } public void stop() { @@ -107,7 +98,7 @@ public class CmtsPcmmClientHandler extends AbstractPCMMClient implements IPCMMCl final COPSKATimer kt = acceptMsg.getKATimer(); if (kt == null) throw new COPSPepException("Mandatory COPS object missing (KA Timer)"); - short kaTimeVal = kt.getTimerVal(); + final short kaTimeVal = kt.getTimerVal(); // ACTimer final COPSAcctTimer at = acceptMsg.getAcctTimer(); @@ -122,8 +113,7 @@ public class CmtsPcmmClientHandler extends AbstractPCMMClient implements IPCMMCl sendRequest(reqMsg); // Create the connection manager - final PcmmCmtsConnection conn = new PcmmCmtsConnection(CLIENT_TYPE, getSocket(), gateConfig, - cmStatus); + final PcmmCmtsConnection conn = new PcmmCmtsConnection(CLIENT_TYPE, getSocket(), config); conn.addRequestState(handle, new CmtsDataProcessor()); conn.setKaTimer(kaTimeVal); conn.setAcctTimer(acctTimer); diff --git a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsPepReqStateMan.java b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsPepReqStateMan.java index 959b7a2..f2bb2be 100644 --- a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsPepReqStateMan.java +++ b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsPepReqStateMan.java @@ -8,72 +8,182 @@ package org.pcmm.rcd.impl; +import static com.google.common.base.Preconditions.checkNotNull; + +import com.google.common.base.Objects; +import com.google.common.primitives.Bytes; +import java.io.IOException; +import java.net.Socket; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Random; +import java.util.Set; +import java.util.UUID; +import java.util.Vector; +import org.pcmm.base.impl.PCMMBaseObject; +import org.pcmm.gates.IAMID; +import org.pcmm.gates.IClassifier; +import org.pcmm.gates.IGateID; +import org.pcmm.gates.IGateSpec; import org.pcmm.gates.IGateSpec.Direction; +import org.pcmm.gates.IGateState; import org.pcmm.gates.IPCMMError; import org.pcmm.gates.IPCMMError.ErrorCode; +import org.pcmm.gates.ISubscriberID; +import org.pcmm.gates.ITransactionID; +import org.pcmm.gates.impl.AMID; +import org.pcmm.gates.impl.DOCSISServiceClassNameTrafficProfile; import org.pcmm.gates.impl.GateID; +import org.pcmm.gates.impl.GateSpec; +import org.pcmm.gates.impl.GateState; +import org.pcmm.gates.impl.GateTimeInfo; +import org.pcmm.gates.impl.GateUsageInfo; import org.pcmm.gates.impl.PCMMError; import org.pcmm.gates.impl.PCMMGateReq; +import org.pcmm.gates.impl.TransactionID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.umu.cops.prpep.COPSPepException; import org.umu.cops.prpep.COPSPepMsgSender; import org.umu.cops.prpep.COPSPepReqStateMan; -import org.umu.cops.stack.*; +import org.umu.cops.stack.COPSClientSI; +import org.umu.cops.stack.COPSContext; +import org.umu.cops.stack.COPSData; +import org.umu.cops.stack.COPSDecision; import org.umu.cops.stack.COPSDecision.DecisionFlag; +import org.umu.cops.stack.COPSDecisionMsg; +import org.umu.cops.stack.COPSException; +import org.umu.cops.stack.COPSHandle; +import org.umu.cops.stack.COPSMsgParser; import org.umu.cops.stack.COPSObjHeader.CNum; import org.umu.cops.stack.COPSObjHeader.CType; +import org.umu.cops.stack.COPSReportMsg; +import org.umu.cops.stack.COPSReportType; import org.umu.cops.stack.COPSReportType.ReportType; -import java.io.IOException; -import java.net.Socket; -import java.util.*; - /** * PEP State manager implementation for use in a CMTS. */ public class CmtsPepReqStateMan extends COPSPepReqStateMan { - private final static Logger logger = LoggerFactory.getLogger(CmtsPepReqStateMan.class); + private static final Logger logger = LoggerFactory.getLogger(CmtsPepReqStateMan.class); - /** - * The configured gates - */ - private final Map> gateConfig; + private final CMTSConfig config; - /** - * The connected CMTSs and whether or not they are up - */ - private final Map cmStatus; + private final Map gateStateMap; - /** - * Contains the gates that have been set where the key is the gate name and the value is a Set of subIds - * that are using this gate - */ - private final Map> gatesSetMap; + private static class GateMetaData { + + private final PCMMGateReq gateReq; + private long commitTime; + private long kiloBytesTransmitted; + + private Random random; + + public GateMetaData(final PCMMGateReq gateReq) { + this.gateReq = checkNotNull(gateReq); + updateCommitTime(); + kiloBytesTransmitted = 0; + this.random = new Random(gateReq.getGateID().getGateID()); + } + + public long updateCommitTime() { + commitTime = System.currentTimeMillis() / 1000L; + return commitTime; + } + + public PCMMGateReq getGateReq() { + return gateReq; + } + + public long getCommitTime() { + return commitTime; + } + + public int getCommitDuration() { + return (int)((System.currentTimeMillis() / 1000L) - commitTime); + } + + public long updateKiloBytesTransmitted() { + kiloBytesTransmitted += random.nextInt(2000); + return kiloBytesTransmitted; + } + + public long getKiloBytesTransmitted() { + return kiloBytesTransmitted; + } + } + + private static class GateKey { + private IAMID amID; + private ISubscriberID subscriberID; + private IGateID gateID; + + public GateKey(final IAMID amID, final ISubscriberID subscriberID, final IGateID gateID) { + this.amID = checkNotNull(amID); + this.subscriberID = checkNotNull(subscriberID); + this.gateID = checkNotNull(gateID); + } + + public IAMID getAmID() { + return amID; + } + + public ISubscriberID getSubscriberID() { + return subscriberID; + } + + public IGateID getGateID() { + return gateID; + } + + public boolean matches(final AMID otherAMID, final ISubscriberID otherSubscriberID) { + checkNotNull(otherAMID); + checkNotNull(otherSubscriberID); + + return Objects.equal(amID, otherAMID) && Objects.equal(subscriberID, otherSubscriberID); + + } + + @Override + public boolean equals(final Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + final GateKey otherGateKey = (GateKey) o; + return Objects.equal(amID, otherGateKey.amID) && + Objects.equal(subscriberID, otherGateKey.subscriberID) && + Objects.equal(gateID, otherGateKey.gateID); + } + + @Override + public int hashCode() { + return Objects.hashCode(amID, subscriberID, gateID); + } + } /** * Create a State Request Manager * - * @param clientType - the client type for this connection - * @param clientHandle - the client-handle for this connection - * @param process - the data processor - * @param socket - the socket connection - * @param gateConfig - the configured service class names (gates) + * @param clientType + * - the client type for this connection + * @param clientHandle + * - the client-handle for this connection + * @param process + * - the data processor + * @param socket + * - the socket connection */ public CmtsPepReqStateMan(final short clientType, final COPSHandle clientHandle, final CmtsDataProcessor process, - final Socket socket, final Map> gateConfig, - final Map cmStatus) { + final Socket socket, final CMTSConfig config) { super(clientType, clientHandle, process, socket, new COPSPepMsgSender(clientType, clientHandle, socket)); - this.gateConfig = Collections.unmodifiableMap(gateConfig); - this.cmStatus = Collections.unmodifiableMap(cmStatus); - - this.gatesSetMap = new HashMap<>(); - for (final Set gateIdSet: gateConfig.values()) { - for (final String gateId : gateIdSet) { - gatesSetMap.put(gateId, new HashSet()); - } - } + this.config = checkNotNull(config); + gateStateMap = new HashMap<>(); } @Override @@ -84,19 +194,23 @@ public class CmtsPepReqStateMan extends COPSPepReqStateMan { final Map removeDecs = new HashMap<>(); final Map installDecs = new HashMap<>(); - for (final Set copsDecisions: decisions.values()) { + for (final Set copsDecisions : decisions.values()) { final COPSDecision cmddecision = copsDecisions.iterator().next(); + logger.debug("decision command: " + cmddecision.getCommand()); switch (cmddecision.getCommand()) { case INSTALL: for (final COPSDecision decision : copsDecisions) { if (decision.getFlag().equals(DecisionFlag.REQERROR)) { - logger.info("processing decision"); + logger.info("processing decision: " + dMsg.getDecSI()); // This is assuming a gate set right or wrong if (dMsg.getDecisions().size() == 1 && dMsg.getDecSI() != null) { final PCMMGateReq gateReq = PCMMGateReq.parse(dMsg.getDecSI().getData().getData()); - if (gateReq.getGateSpec() != null) { + if (gateReq != null) { processGateReq(gateReq, _socket); } + else { + logger.error("gateReq failed to parse"); + } } } } @@ -137,72 +251,321 @@ public class CmtsPepReqStateMan extends COPSPepReqStateMan { // TODO - Check and/or Set state here // Gate ADD gateReq.getTrafficProfile() != null // Gate REMOVE gateReq.getTrafficProfile() == null - final String subId = gateReq.getSubscriberID().getSourceIPAddress().getHostAddress(); - // Get direction here + switch (gateReq.getTransactionID().getGateCommandType()) { + case GATE_SET: + processGateSet(gateReq, socket); + break; + case GATE_INFO: + processGateInfo(gateReq, socket); + break; + default: + logger.error("Emulator does not support gate command: {}", + gateReq.getTransactionID().getGateCommandType()); + } + + } + + + private IPCMMError checkForMissingObjects(final PCMMGateReq gateReq) { + // In cases where multiple valid alternatives exist for the S-Type of a missing object, + // this portion of the Error-Subcode MUST be set to zero. + + if (gateReq.getTransactionID() == null) { + final short subCode = + COPSMsgParser.bytesToShort(PCMMBaseObject.SNum.TRANSACTION_ID.getValue(), TransactionID.STYPE); + return new PCMMError(ErrorCode.MISSING_REQ_OBJ, subCode); + } + + final ITransactionID.GateCommandType gateCommand = gateReq.getTransactionID().getGateCommandType(); + + if (gateCommand == ITransactionID.GateCommandType.GATE_SET) { + // Gate set does not allow gateID + if (gateReq.getGateID() != null) { + final short subCode = COPSMsgParser.bytesToShort(PCMMBaseObject.SNum.GATE_ID.getValue(), GateID.STYPE); + return new PCMMError(ErrorCode.MISSING_REQ_OBJ, subCode); + } + + if (gateReq.getTrafficProfile() == null) { + final short subCode = COPSMsgParser.bytesToShort(PCMMBaseObject.SNum.TRAFFIC_PROFILE.getValue(), (byte) 0); + return new PCMMError(ErrorCode.MISSING_REQ_OBJ, subCode); + } + + if (gateReq.getClassifiers() == null || gateReq.getClassifiers().isEmpty()) { + final short subCode = COPSMsgParser.bytesToShort(PCMMBaseObject.SNum.CLASSIFIERS.getValue(), (byte) 0); + return new PCMMError(ErrorCode.MISSING_REQ_OBJ, subCode); + } + + if (gateReq.getGateSpec() == null) { + final short subCode = COPSMsgParser.bytesToShort(PCMMBaseObject.SNum.GATE_SPEC.getValue(), GateSpec.STYPE); + return new PCMMError(ErrorCode.MISSING_REQ_OBJ, subCode); + } + + final IGateSpec gateSpec = gateReq.getGateSpec(); + if (gateSpec.getDirection() == null) { + return new PCMMError(ErrorCode.INVALID_FIELD); + } + + } + else { + + if (gateReq.getGateID() == null) { + final short subCode = COPSMsgParser.bytesToShort(PCMMBaseObject.SNum.GATE_ID.getValue(), GateID.STYPE); + return new PCMMError(ErrorCode.MISSING_REQ_OBJ, subCode); + } + } + + if (gateReq.getAMID() == null) { + final short subCode = COPSMsgParser.bytesToShort(PCMMBaseObject.SNum.AMID.getValue(), AMID.STYPE); + return new PCMMError(ErrorCode.MISSING_REQ_OBJ, subCode); + } + + if (gateReq.getSubscriberID() == null || gateReq.getSubscriberID().getSourceIPAddress() == null + || gateReq.getSubscriberID().getSourceIPAddress().getHostAddress() == null) { + final short subCode = COPSMsgParser.bytesToShort(PCMMBaseObject.SNum.SUBSCRIBER_ID.getValue(), (byte) 0); + return new PCMMError(ErrorCode.MISSING_REQ_OBJ, subCode); + } + + + return null; + } + + private IPCMMError checkForInvalidObjects(final PCMMGateReq gateReq) { + final ITransactionID.GateCommandType gateCommand = gateReq.getTransactionID().getGateCommandType(); + + // GateID + if (gateCommand == ITransactionID.GateCommandType.GATE_INFO) { + if (!gateStateMap.containsKey(gateReq.getGateID())) { + return new PCMMError(ErrorCode.UNK_GATE_ID); + } + } + else { + // Traffic profile + + if (gateReq.getTrafficProfile() instanceof DOCSISServiceClassNameTrafficProfile) { + final DOCSISServiceClassNameTrafficProfile scnTrafficProfile = + (DOCSISServiceClassNameTrafficProfile) gateReq.getTrafficProfile(); + + Set directionSCNs; + if (gateReq.getGateSpec().getDirection().equals(Direction.DOWNSTREAM)) { + directionSCNs = config.getDownstreamServiceClassNames(); + } else { + directionSCNs = config.getUpstreamServiceClassNames(); + } + if (!directionSCNs.contains(scnTrafficProfile.getScnName())) { + return new PCMMError(ErrorCode.UNDEF_SCN_NAME); + } + } else { + // TODO remote this after other profiles are supported + logger.error("Currently only DOCSIS Service Class Name Traffic Profiles are supported: attempted {}", + gateReq.getTrafficProfile().getClass().getName()); + return new PCMMError(ErrorCode.OTHER_UNSPECIFIED); + } + + // number of classifiers + if (config.getNumberOfSupportedClassifiers() < gateReq.getClassifiers().size()) { + return new PCMMError(ErrorCode.NUM_CLASSIFIERS, config.getNumberOfSupportedClassifiers()); + } + } + + + + // SubscriberID + String subId = gateReq.getSubscriberID().getSourceIPAddress().getHostAddress(); + if(!config.getModemStatus().containsKey(subId) || !config.getModemStatus().get(subId)) { + return new PCMMError(ErrorCode.INVALID_SUB_ID); + } + + // Iff the gate exists + if (gateReq.getGateID() != null + && gateStateMap.containsKey(gateReq.getGateID())) { + GateMetaData existingGate = gateStateMap.get(gateReq.getGateID()); + + // Unauthorized AMID - only the AM that created a gate may change it + if (!existingGate.getGateReq().getAMID().equals(gateReq.getAMID())) { + return new PCMMError(ErrorCode.UNAUTH_AMID); + } + } + + + + + return null; + } + + private IPCMMError getGateError(final PCMMGateReq gateReq) { + + IPCMMError error = null; + error = checkForMissingObjects(gateReq); + if (error != null) { + return error; + } + + error = checkForInvalidObjects(gateReq); + if (error != null) { + return error; + } + + + return null; + } + + private void processGateSet(final PCMMGateReq gateReq, final Socket socket) throws COPSException { + + final String subId = gateReq.getSubscriberID().getSourceIPAddress().getHostAddress(); final Direction gateDir = gateReq.getGateSpec().getDirection(); - final Set gateNames = gateConfig.get(gateDir); - // TODO - Determine if this is the best means to derive the gate name??? - final String gateName = new String(gateReq.getTrafficProfile().getAsBinaryArray()); - - final IPCMMError error; - if (subId == null || gateDir == null || gateNames == null) { - // Missing required object - // TODO - Determine if this is the correct code. 3 was being used previously and I don't see any corresponding code. - error = new PCMMError(ErrorCode.UNK_GATE_ID); - } else if (!cmStatus.keySet().contains(subId) - || (cmStatus.keySet().contains(subId) && !cmStatus.get(subId))) { - // Invalid Object - // TODO - Determine if this code is correct - error = new PCMMError(ErrorCode.INVALID_SUB_ID); - } else if (!gateNames.contains(gateName.trim())) { - // TODO - Determine if this code is correct - error = new PCMMError(ErrorCode.UNDEF_SCN_NAME); + + final String serviceClassName; + if ((gateReq.getTrafficProfile() instanceof DOCSISServiceClassNameTrafficProfile)) { + serviceClassName = ((DOCSISServiceClassNameTrafficProfile) gateReq.getTrafficProfile()).getScnName(); } else { - error = null; - gatesSetMap.get(gateName.trim()).add(subId); + serviceClassName = null; } - gateReq.setError(error); - logger.info("Processing gate request [" + gateName + "] with direction [" + gateDir + ']'); + final IPCMMError error = getGateError(gateReq); + gateReq.setError(error); - // Get gate name + logger.info("Processing gate set request [" + serviceClassName + "] with direction [" + gateDir + ']'); // Set response + + final ITransactionID.GateCommandType gateCommand = (error == null) + ? ITransactionID.GateCommandType.GATE_SET_ACK + : ITransactionID.GateCommandType.GATE_SET_ERR; + + final TransactionID transactionID = + new TransactionID(gateReq.getTransactionID().getTransactionIdentifier(), gateCommand); + final List data = new ArrayList<>(); - for (final byte val : gateReq.getTransactionID().getAsBinaryArray()) - data.add(val); - for (final byte val : gateReq.getAMID().getAsBinaryArray()) - data.add(val); - for (final byte val : gateReq.getSubscriberID().getAsBinaryArray()) - data.add(val); - if (error != null) for (final byte val : gateReq.getError().getAsBinaryArray()) - data.add(val); + addBytesToList(transactionID.getAsBinaryArray(), data); + addBytesToList(gateReq.getAMID().getAsBinaryArray(), data); + addBytesToList(gateReq.getSubscriberID().getAsBinaryArray(), data); - // Assign a gate ID - final GateID gateID = new GateID(UUID.randomUUID().hashCode()); - for (final byte val : gateID.getAsBinaryArray()) - data.add(val); + if (error == null) { + // Assign a gate ID + final GateID gateID = new GateID(UUID.randomUUID().hashCode()); + for (final byte val : gateID.getAsBinaryArray()) { + data.add(val); + } + gateReq.setGateID(gateID); + + int timeStamp = (int)(System.currentTimeMillis() / 1000L); + gateReq.setGateTimeInfo(new GateTimeInfo(timeStamp)); - final byte[] csiArr = new byte[data.size()]; - for (int i = 0; i < data.size(); i++) { - csiArr[i] = data.get(i); + gateStateMap.put(gateID, new GateMetaData(gateReq)); + } + else { + addBytesToList(error.getAsBinaryArray(), data); } + + final byte[] csiArr = Bytes.toArray(data); final COPSClientSI si = new COPSClientSI(CNum.CSI, CType.DEF, new COPSData(csiArr, 0, csiArr.length)); final ReportType reportType; - if (gateReq.getError() == null) reportType = ReportType.SUCCESS; else reportType = ReportType.FAILURE; + if (gateReq.getError() == null) { + reportType = ReportType.SUCCESS; + } else { + reportType = ReportType.FAILURE; + } - logger.info("Returning " + reportType + " for gate request [" + gateName + "] direction [" + gateDir + logger.info("Returning " + reportType + " for gate request [" + serviceClassName + "] direction [" + gateDir + "] for host - " + subId); - final COPSReportMsg reportMsg = new COPSReportMsg(_clientType, getClientHandle(), - new COPSReportType(reportType), si, null); + sendReport(reportType, si, socket); + + } + + private void processGateInfo(final PCMMGateReq gateReq, final Socket socket) throws COPSException { + logger.info("GateInfo"); + + IPCMMError error = getGateError(gateReq); + + final TransactionID transactionID; + final ReportType reportType; + if (error != null) { + transactionID = new TransactionID(gateReq.getTransactionID().getTransactionIdentifier(), + ITransactionID.GateCommandType.GATE_INFO_ERR); + reportType = ReportType.FAILURE; + } + else { + transactionID = new TransactionID(gateReq.getTransactionID().getTransactionIdentifier(), + ITransactionID.GateCommandType.GATE_INFO_ACK); + reportType = ReportType.SUCCESS; + } + + final List data = new ArrayList<>(); + addBytesToList(transactionID.getAsBinaryArray(), data); + addBytesToList(gateReq.getAMID().getAsBinaryArray(), data); + addBytesToList(gateReq.getSubscriberID().getAsBinaryArray(), data); + addBytesToList(gateReq.getGateID().getAsBinaryArray(), data); + + if (error != null) { + addBytesToList(error.getAsBinaryArray(), data); + } + else { + GateMetaData exisitingGate = gateStateMap.get(gateReq.getGateID()); + + addBytesToList(exisitingGate.getGateReq().getGateSpec().getAsBinaryArray(), data); + + for (IClassifier classifier : exisitingGate.getGateReq().getClassifiers()) { + addBytesToList(classifier.getAsBinaryArray(), data); + } + + addBytesToList(exisitingGate.getGateReq().getTrafficProfile().getAsBinaryArray(), data); + + GateTimeInfo timeInfo = new GateTimeInfo(exisitingGate.getCommitDuration()); + addBytesToList(timeInfo.getAsBinaryArray(), data); + + GateUsageInfo gateUsageInfo = new GateUsageInfo(exisitingGate.updateKiloBytesTransmitted()); + addBytesToList(gateUsageInfo.getAsBinaryArray(), data); + + GateState gateState = new GateState(IGateState.GateStateType.COMMITTED, + IGateState.GateStateReasonType.OTHER); + addBytesToList(gateState.getAsBinaryArray(), data); + + logger.info("Returning " + reportType + " for gate info request on gate " + exisitingGate.getGateReq().getGateID() ); + } + + final byte[] csiArr = Bytes.toArray(data); + COPSClientSI copsClientSI = new COPSClientSI(CNum.CSI, CType.DEF, new COPSData(csiArr, 0, csiArr.length)); + + sendReport(reportType, copsClientSI, socket); + } + + private void sendReport(ReportType reportType, COPSClientSI copsClientSI, final Socket socket) + throws COPSPepException { + logger.info("Returning {} for gate request", reportType); + + final COPSReportMsg reportMsg = + new COPSReportMsg(_clientType, getClientHandle(), new COPSReportType(reportType), copsClientSI, null); try { reportMsg.writeData(socket); } catch (IOException e) { throw new COPSPepException("Error writing gate set SUCCESS Report", e); } + + } + + private static void addBytesToList(byte[] array, List list) { + checkNotNull(array); + checkNotNull(list); + + if (array.length == 0) return; + + // if list supports resizing do so + if (list instanceof ArrayList) { + ArrayList arrayList = (ArrayList) list; + arrayList.ensureCapacity(list.size() + array.length); + } + else if (list instanceof Vector){ + Vector vector = (Vector) list; + vector.ensureCapacity(vector.size() + array.length); + } + + // Add all + for (byte b : array) { + list.add(b); + } } } diff --git a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsYmal.java b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsYmal.java new file mode 100644 index 0000000..13b5413 --- /dev/null +++ b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/CmtsYmal.java @@ -0,0 +1,101 @@ +/* + * Copyright (c) 2015 CableLabs and others. All rights reserved. + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License v1.0 which accompanies this distribution, + * and is available at http://www.eclipse.org/legal/epl-v10.html + */ + +package org.pcmm.rcd.impl; + +import com.fasterxml.jackson.annotation.JsonProperty; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import org.pcmm.gates.IGateSpec; + +/** + * @author rvail + */ +public class CmtsYmal { + @JsonProperty("port") + private int port; + + @JsonProperty("numberOfSupportedClassifiers") + private short numberOfSupportedClassifiers; + + @JsonProperty("serviceClassNames") + private Collection serviceClassNames; + + @JsonProperty("cmStatuses") + private Collection cmStatuses; + + public int getPort() { + return port; + } + + public short getNumberOfSupportedClassifiers() { + return numberOfSupportedClassifiers; + } + + public Map> getServiceClassNames() { + final Map> out = new HashMap<>(); + + for (final ServiceClassNamesYaml scns : serviceClassNames) { + Set names; + if (out.containsKey(scns.direction)) { + names = out.get(scns.direction); + } else { + names = new HashSet<>(scns.gateNames.size()); + out.put(scns.direction, names); + } + names.addAll(scns.gateNames); + } + return out; + } + + public Map getCmStatus() { + final Map out = new HashMap<>(); + + for (final CmStatusYaml cmStatus : cmStatuses) { + out.put(cmStatus.hostIp, cmStatus.status); + } + + return out; + } + + + /** + * Class to hold the YAML gate configuration values + */ + public static class ServiceClassNamesYaml { + @JsonProperty("direction") + private IGateSpec.Direction direction; + + @JsonProperty("names") + private Set gateNames; + + public IGateSpec.Direction getDirection() { + return direction; + } + + public Set getGateNames() { + return Collections.unmodifiableSet(gateNames); + } + } + + + /** + * Class to hold the YAML Cable Modem configuration values + */ + public static class CmStatusYaml { + @JsonProperty("host") + private String hostIp; + + @JsonProperty("status") + private boolean status; + } +} diff --git a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/PcmmCmtsConnection.java b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/PcmmCmtsConnection.java index ee11316..f9a49a3 100644 --- a/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/PcmmCmtsConnection.java +++ b/packetcable-emulator/src/main/java/org/pcmm/rcd/impl/PcmmCmtsConnection.java @@ -8,6 +8,8 @@ package org.pcmm.rcd.impl; +import static com.google.common.base.Preconditions.checkNotNull; + import org.pcmm.gates.IGateSpec.Direction; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -28,37 +30,26 @@ import java.util.Set; */ class PcmmCmtsConnection extends COPSPepConnection { - private final static Logger logger = LoggerFactory.getLogger(COPSPepConnection.class); - - /** - * The configured gates - */ - private final Map> gateConfig; + private static final Logger logger = LoggerFactory.getLogger(COPSPepConnection.class); - /** - * The connected CMTSs and whether or not they are up - */ - private final Map cmStatus; + private final CMTSConfig config; /** * Constructor * @param clientType - the client-type * @param sock - the socket connection - * @param gateConfig - the configured gates - * @param cmStatus - the configured CMs and whether or each is connected + * @param config - emulator configuration */ - public PcmmCmtsConnection(final short clientType, final Socket sock, final Map> gateConfig, - final Map cmStatus) { + public PcmmCmtsConnection(final short clientType, final Socket sock, final CMTSConfig config) { super(clientType, sock); - this.gateConfig = Collections.unmodifiableMap(gateConfig); - this.cmStatus = Collections.unmodifiableMap(cmStatus); + this.config = checkNotNull(config); } @Override public COPSPepReqStateMan addRequestState(final COPSHandle clientHandle, final COPSPepDataProcess process) throws COPSException { final COPSPepReqStateMan manager = new CmtsPepReqStateMan(_clientType, clientHandle, (CmtsDataProcessor)process, - _sock, gateConfig, cmStatus); + _sock, config); if (_managerMap.get(clientHandle) != null) throw new COPSPepException("Duplicate Handle, rejecting " + clientHandle); diff --git a/packetcable-policy-model/src/main/yang/packetcable.yang b/packetcable-policy-model/src/main/yang/packetcable.yang index df2f0e5..ba85532 100644 --- a/packetcable-policy-model/src/main/yang/packetcable.yang +++ b/packetcable-policy-model/src/main/yang/packetcable.yang @@ -209,23 +209,35 @@ module packetcable } grouping gate-operational-attributes { - leaf gatePath { - config false; - type string; - description "FQ Gate path app/subscriber/gate"; - mandatory true; - } - leaf ccapId { - config false; - type string; - description "CCAP Identity"; - mandatory true; - } - leaf cops-state { - config false; - type string; - description "Gate operational COPS state"; - mandatory true; + leaf gatePath { + config false; + type string; + description "FQ Gate path app/subscriber/gate"; + mandatory true; + } + leaf ccapId { + config false; + type string; + description "CCAP Identity"; + mandatory true; + } + leaf cops-gate-state { + config false; + type string; + description "Operational COPS Gate state"; + mandatory true; + } + leaf cops-gate-time-info { + config false; + type string; + description "Operational COPS Gate time info"; + mandatory true; + } + leaf cops-gate-usage-info { + config false; + type string; + description "Operational COPS gate usage info"; + mandatory true; } leaf cops-gateId { config false; @@ -450,5 +462,95 @@ module packetcable } } -} + //RPCs + rpc ccap-set-connection { + input { + leaf ccapId { + type instance-identifier; + ext:context-reference ccap-context; + } + container connection { + leaf connected { + type boolean; + description "COPS session state"; + } +// leaf idle-detect { +// type uint8; +// description "COPS connection idle timer"; +// } + } + } + output { + container ccap { + leaf ccapId { + type string; + } + container connection { + uses ccap-connection; + } + } + leaf response { + type string; + } + leaf timestamp { + type yang:date-and-time; + description "RPC timestamp"; + } + } + } + + rpc ccap-poll-connection { + input { + leaf ccapId { + type instance-identifier; + ext:context-reference ccap-context; + } + } + output { + container ccap { + leaf ccapId { + type string; + } + container connection { + uses ccap-connection; + } + } + leaf response { + type string; + } + leaf timestamp { + type yang:date-and-time; + description "RPC timestamp"; + } + } + } + rpc qos-poll-gates { + input { + leaf appId { + type instance-identifier; + ext:context-reference app-context; + } + leaf subscriberId { + type string; + description "Subscriber Identity -- must be a CM or CPE IP address"; + } + leaf gateId { + type string; + description "Qos Gate Identity"; + } + } + output { + container gate { + uses gate-operational-attributes; + } + leaf response { + type string; + } + leaf timestamp { + type yang:date-and-time; + description "RPC timestamp"; + } + } + } +} \ No newline at end of file diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMGateReqBuilder.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMGateReqBuilder.java index f944c1d..98e1d27 100644 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMGateReqBuilder.java +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMGateReqBuilder.java @@ -39,6 +39,9 @@ import org.pcmm.gates.ITrafficProfile; import org.pcmm.gates.impl.AMID; import org.pcmm.gates.impl.DOCSISServiceClassNameTrafficProfile; import org.pcmm.gates.impl.GateID; +import org.pcmm.gates.impl.GateState; +import org.pcmm.gates.impl.GateTimeInfo; +import org.pcmm.gates.impl.GateUsageInfo; import org.pcmm.gates.impl.PCMMError; import org.pcmm.gates.impl.PCMMGateReq; import org.pcmm.gates.impl.SubscriberID; @@ -61,9 +64,13 @@ public class PCMMGateReqBuilder { private ITrafficProfile trafficProfile = null; private final List classifiers = Lists.newArrayListWithExpectedSize(4); private PCMMError error = null; + private GateState gateState = null; + private GateTimeInfo gateTimeInfo = null; + private GateUsageInfo gateUsageInfo = null; public PCMMGateReq build() { - return new PCMMGateReq(amid, subscriberID, transactionID, gateSpec, trafficProfile, classifiers, gateID, error); + return new PCMMGateReq(amid, subscriberID, transactionID, gateSpec, trafficProfile, classifiers, + gateID, error, gateState, gateTimeInfo, gateUsageInfo); } public void setAmId(final AmId qosAmId) { @@ -140,7 +147,7 @@ public class PCMMGateReqBuilder { final Short index = container.getClassifierId(); if (choice instanceof QosClassifierChoice) { - addClassifier(((QosClassifierChoice) choice).getClassifier()); + addClassifier(index, ((QosClassifierChoice) choice).getClassifier()); } else if (choice instanceof ExtClassifierChoice) { addExtClassifier(index, ((ExtClassifierChoice) choice).getExtClassifier()); @@ -154,7 +161,7 @@ public class PCMMGateReqBuilder { } } - private void addClassifier(final Classifier qosClassifier) { + private void addClassifier(final Short index,final Classifier qosClassifier) { // TODO - try and make these variables immutable Protocol protocol = null; byte tosOverwrite = 0; @@ -164,7 +171,9 @@ public class PCMMGateReqBuilder { short srcPort = (short) 0; short dstPort = (short) 0; byte priority = (byte) 64; - + //byte priority = index.byteValue(); + + // Legacy classifier if (qosClassifier.getProtocol() != null) { protocol = Protocol.valueOf(qosClassifier.getProtocol().getValue().shortValue()); diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java index 16d633b..0368e54 100644 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PCMMService.java @@ -20,7 +20,10 @@ import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.app import org.pcmm.PCMMPdpAgent; import org.pcmm.PCMMPdpDataProcess; import org.pcmm.PCMMPdpMsgSender; +import org.pcmm.gates.IGateState; +import org.pcmm.gates.ITransactionID; import org.pcmm.gates.impl.PCMMGateReq; +import org.pcmm.gates.impl.TransactionID; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.umu.cops.prpdp.COPSPdpException; @@ -47,6 +50,7 @@ public class PCMMService { this.ccap = ccap; ipAddr = ccap.getConnection().getIpAddress(); portNum = ccap.getConnection().getPort(); + ccapClient = new CcapClient(ipAddr, portNum); logger.info("Attempting to add CCAP with ID {} @ {}:{}", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(), portNum.getValue()); @@ -70,10 +74,14 @@ public class PCMMService { } } - public class GateSetStatus { + public class GateSendStatus { private boolean didSucceed = false; private String message = ""; private String copsGateId = ""; + private String copsGateState = ""; + private String copsGateStateReason = ""; + private String copsGateTimeInfo = ""; + private String copsGateUsageInfo = ""; public boolean didSucceed() { return didSucceed; @@ -99,12 +107,39 @@ public class PCMMService { this.copsGateId = copsGateId; } + public String getCopsGateState() { + return copsGateState; + } + void setCopsGateState(final String copsGateState) { + this.copsGateState = copsGateState; + } + + public String getCopsGateStateReason() { + return copsGateStateReason; + } + void setCopsGateStateReason(final String copsGateStateReason) { + this.copsGateStateReason = copsGateStateReason; + } + + public String getCopsGateTimeInfo() { + return copsGateTimeInfo; + } + void setCopsGateTimeInfo(final String copsGateTimeInfo) { + this.copsGateTimeInfo = copsGateTimeInfo; + } + + public String getCopsGateUsageInfo() { + return copsGateUsageInfo; + } + void setCopsGateUsageInfo(final String copsGateUsageInfo) { + this.copsGateUsageInfo = copsGateUsageInfo; + } } - public GateSetStatus sendGateSet(final String gatePathStr, final InetAddress subId, final Gate qosGate, + public GateSendStatus sendGateSet(final String gatePathStr, final InetAddress subId, final Gate qosGate, final ServiceFlowDirection scnDir) { - GateSetStatus status = new GateSetStatus(); + GateSendStatus status = new GateSendStatus(); logger.info("Sending gate to CCAP with ID - " + ccap.getCcapId()); @@ -220,7 +255,110 @@ public class PCMMService { } else { logger.warn("Attempt to delete non-existent gate with path - " + gatePathStr); return false; + } } + + public Boolean getPcmmPdpSocket() { + try { + return ccapClient.pcmmPdp.getSocket().isClosed(); + } catch (Exception e) { + logger.error("getPcmmPdpSocket: {} FAILED: {}", ccapClient, e.getMessage()); + return true; + } + } + + public Boolean getPcmmCcapClientIsConnected() { + try { + return ccapClient.isConnected; + } catch (Exception e) { + logger.error("getPcmmCcapClientIsConnected: {} FAILED: {}", ccapClient, e.getMessage()); + return false; + } + } + + public String getPcmmCcapClientConnectErrMsg() { + try { + return ccapClient.errMessage; + } catch (Exception e) { + logger.error("getPcmmCcapClientIsConnected: {} FAILED: {}", ccapClient, e.getMessage()); + return e.getMessage(); + } + } + + //new gate-info method + public GateSendStatus sendGateInfo(final String gatePathStr) { + + logger.info("sendGateInfo() - " + ccap); + + GateSendStatus status = new GateSendStatus(); + + // recover the original gate request + final PCMMGateReq gateReq = gateRequests.get(gatePathStr); + + // is the ccap socket open? + final Boolean socketIsClosed = getPcmmPdpSocket(); + + if ((gateReq != null) && (!socketIsClosed)) { + gateReq.setTransactionID(new TransactionID(gateReq.getTransactionID().getTransactionIdentifier(), + ITransactionID.GateCommandType.GATE_INFO)); + + ccapClient.sendGateInfo(gateReq); + // and wait for the response to complete + try { + // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to + // TODO - handle this synchronization. + synchronized (gateReq) { + logger.info("Waiting 5000ms for gate request to be updated"); + gateReq.wait(5000); + logger.debug("Gate request error - " + gateReq.getError()); + logger.debug("Gate request ID - " + gateReq.getGateID()); + } + } catch (InterruptedException e) { + status.setDidSucceed(false); + status.setMessage(String.format("Gate-Info Request Timeout for %s", ccap.getCcapId())); + return status; + } + if (gateReq.getError() != null) { + status.setDidSucceed(false); + status.setMessage( + String.format("%s reports '%s'", ccap.getCcapId(), gateReq.getError().toString())); + logger.error("PCMMService: sendGateInfo(): returned error: {}", gateReq.getError().toString()); + } else { + if (gateReq.getGateID() != null) { + status.setDidSucceed(true); + status.setCopsGateId(String.format("%08x", gateReq.getGateID().getGateID())); + //status.setMessage(String.format("200 OK - sendGateInfo for %s/%s returned GateId %08x", + // ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID()) ); + + final IGateState gateState = gateReq.getGateState(); + status.setCopsGateState(gateState.getGateState().toString()); + status.setCopsGateStateReason(gateState.getGateStateReason().toString()); + status.setCopsGateTimeInfo(String.format("%d", gateReq.getGateTimeInfo().getGateTimeInfo())); + status.setCopsGateUsageInfo(String.format("%d", gateReq.getGateUsageInfo().getGateUsageInfo())); + logger.info(String.format("PCMMService: sendGateInfo(): returned GateId %08x: ", + gateReq.getGateID().getGateID())); + } else { + status.setDidSucceed(false); + status.setMessage( + String.format("404 Not Found - sendGateInfo for %s/%s no gateId returned", ccap.getCcapId(), + gatePathStr)); + + logger.info("PCMMService: sendGateInfo(): no gateId returned:"); + } + return status; + } + } else { + status.setDidSucceed(false); + if (socketIsClosed) { + status.setMessage(String.format("%s: CCAP Cops Socket is closed",ccap.getCcapId())); + } + else { + status.setMessage( String.format("Attempt to get info of non-existent gate with path - " + gatePathStr)); + } + return status; + } + return status; + } /** @@ -266,6 +404,7 @@ public class PCMMService { */ public void connect() { logger.info("Attempting to connect to host: " + ipv4 + " port: " + port); + errMessage = null; try { pcmmPdp.connect(); @@ -316,6 +455,17 @@ public class PCMMService { } return true; } + + public Boolean sendGateInfo(final PCMMGateReq gateReq) { + logger.info("CcapClient: sendGateInfo(): {}:{} => {}", ipv4, port); + try { + pcmmSender.sendGateInfo(gateReq); + } catch (COPSPdpException e) { + logger.error("CcapClient: sendGateInfo(): {}:{} => {} FAILED: {}", ipv4, port, + e.getMessage()); + } + return true; + } } } diff --git a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PacketcableProvider.java b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PacketcableProvider.java index 9c1b690..d27b9e9 100644 --- a/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PacketcableProvider.java +++ b/packetcable-policy-server/src/main/java/org/opendaylight/controller/packetcable/provider/PacketcableProvider.java @@ -14,19 +14,28 @@ import com.google.common.base.Optional; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.util.concurrent.CheckedFuture; +import com.google.common.util.concurrent.Futures; + import java.net.InetAddress; import java.net.UnknownHostException; +import java.text.DateFormat; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; +import java.util.Date; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.Future; + import javax.annotation.Nonnull; import javax.annotation.concurrent.ThreadSafe; + import org.opendaylight.controller.md.sal.binding.api.DataBroker; import org.opendaylight.controller.md.sal.binding.api.DataChangeListener; import org.opendaylight.controller.md.sal.binding.api.WriteTransaction; @@ -38,11 +47,25 @@ import org.opendaylight.controller.packetcable.provider.validation.Validator; import org.opendaylight.controller.packetcable.provider.validation.impl.CcapsValidatorProviderFactory; import org.opendaylight.controller.packetcable.provider.validation.impl.QosValidatorProviderFactory; import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.ProviderContext; +import org.opendaylight.controller.sal.binding.api.BindingAwareBroker.RoutedRpcRegistration; import org.opendaylight.controller.sal.binding.api.BindingAwareProvider; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpPrefix; import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Prefix; +import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.yang.types.rev100924.DateAndTime; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.AppContext; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.CcapContext; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.CcapPollConnectionInput; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.CcapPollConnectionOutput; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.CcapPollConnectionOutputBuilder; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.CcapSetConnectionInput; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.CcapSetConnectionOutput; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.CcapSetConnectionOutputBuilder; import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.Ccaps; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.PacketcableService; import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.Qos; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.QosPollGatesInput; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.QosPollGatesOutput; +import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.QosPollGatesOutputBuilder; import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceClassName; import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceFlowDirection; import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.attributes.ConnectionBuilder; @@ -65,6 +88,8 @@ import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.app import org.opendaylight.yangtools.concepts.ListenerRegistration; import org.opendaylight.yangtools.yang.binding.DataObject; import org.opendaylight.yangtools.yang.binding.InstanceIdentifier; +import org.opendaylight.yangtools.yang.common.RpcResult; +import org.opendaylight.yangtools.yang.common.RpcResultBuilder; import org.pcmm.rcd.IPCMMClient; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -74,9 +99,10 @@ import org.slf4j.LoggerFactory; *

* This class is responsible for processing messages received from ODL's restconf interface. * TODO - Remove some of these state maps and move some of this into the PCMMService + * TODO Don't implement PacketcableService, move that into an inner class */ @ThreadSafe -public class PacketcableProvider implements BindingAwareProvider, AutoCloseable { +public class PacketcableProvider implements BindingAwareProvider, AutoCloseable, PacketcableService { private static final Logger logger = LoggerFactory.getLogger(PacketcableProvider.class); @@ -105,6 +131,9 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable private DataBroker dataBroker; private MdsalUtils mdsalUtils; + //Routed RPC Registration + private RoutedRpcRegistration rpcRegistration; + // Data change listeners/registrations private final CcapsDataChangeListener ccapsDataChangeListener = new CcapsDataChangeListener(); private final QosDataChangeListener qosDataChangeListener = new QosDataChangeListener(); @@ -129,13 +158,17 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable mdsalUtils = new MdsalUtils(dataBroker); - ccapsDataChangeListenerRegistration = dataBroker - .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, ccapsIID.child(Ccap.class), + ccapsDataChangeListenerRegistration = + dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, ccapsIID.child(Ccap.class), ccapsDataChangeListener, DataBroker.DataChangeScope.SUBTREE); - qosDataChangeListenerRegistration = dataBroker - .registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, PacketcableProvider.qosIID.child(Apps.class).child(App.class), - qosDataChangeListener, DataBroker.DataChangeScope.SUBTREE); + qosDataChangeListenerRegistration = dataBroker.registerDataChangeListener(LogicalDatastoreType.CONFIGURATION, + PacketcableProvider.qosIID.child(Apps.class).child(App.class), qosDataChangeListener, + DataBroker.DataChangeScope.SUBTREE); + + rpcRegistration = session.addRoutedRpcImplementation(PacketcableService.class, this); + logger.info("onSessionInitiated().rpcRgistration: {}", rpcRegistration); + } /** @@ -294,44 +327,35 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable // type match between iid and badData is done at start of loop - @SuppressWarnings("unchecked") - final InstanceIdentifier ccapIID = (InstanceIdentifier) iid; + @SuppressWarnings("unchecked") final InstanceIdentifier ccapIID = (InstanceIdentifier) iid; writeTransaction.put(LogicalDatastoreType.OPERATIONAL, ccapIID, opperationalCcap); - } - else if (badData instanceof Gate) { + } else if (badData instanceof Gate) { final Gate gate = (Gate) badData; final Gate operationalGate = - new GateBuilder() - .setGateId(gate.getGateId()) - .setError(exception.getErrorMessages()) - .build(); + new GateBuilder().setGateId(gate.getGateId()).setError(exception.getErrorMessages()).build(); - final Gates operationalGates = new GatesBuilder() - .setGate(Collections.singletonList(operationalGate)) - .build(); + final Gates operationalGates = + new GatesBuilder().setGate(Collections.singletonList(operationalGate)).build(); final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(iid.firstIdentifierOf(Subscriber.class)); - final Subscriber operationalSubscriber = new SubscriberBuilder() - .setSubscriberId(subscriberKey.getSubscriberId()) - .setGates(operationalGates) - .build(); + final Subscriber operationalSubscriber = + new SubscriberBuilder().setSubscriberId(subscriberKey.getSubscriberId()) + .setGates(operationalGates) + .build(); - final Subscribers operationalSubscribers = new SubscribersBuilder() - .setSubscriber(Collections.singletonList(operationalSubscriber)) - .build(); + final Subscribers operationalSubscribers = + new SubscribersBuilder().setSubscriber(Collections.singletonList(operationalSubscriber)) + .build(); final InstanceIdentifier appIID = iid.firstIdentifierOf(App.class); final AppKey appKey = InstanceIdentifier.keyOf(appIID); - final App operationalApp = new AppBuilder() - .setAppId(appKey.getAppId()) - .setSubscribers(operationalSubscribers) - .build(); + final App operationalApp = + new AppBuilder().setAppId(appKey.getAppId()).setSubscribers(operationalSubscribers).build(); writeTransaction.put(LogicalDatastoreType.OPERATIONAL, appIID, operationalApp); - } - else { + } else { // If you get here a developer forgot to add a type above logger.error("Unexpected type requested for error saving: {}", badData); throw new IllegalStateException("Unsupported type for error saving"); @@ -364,6 +388,7 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable } } + /** * Removes Subscriber if all Gate instances are removed */ @@ -401,6 +426,9 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable @Override void postRemove(final InstanceIdentifier appIID) { + //unregister app rpc path + logger.info("Un-Registering App Routed RPC Path..."); + rpcRegistration.unregisterPath(AppContext.class, appIID); executor.execute(new AppsCleaner(appIID)); } } @@ -409,7 +437,7 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable /** * Removes Apps if all App instances are removed. */ - private class AppsCleaner extends AbstractCleaner { + private class AppsCleaner extends AbstractCleaner { public AppsCleaner(InstanceIdentifier removedAppIID) { super(removedAppIID, Apps.class, LogicalDatastoreType.OPERATIONAL); @@ -424,12 +452,14 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable /** * Helper class to do the heavy lifting in removing object. Lets subclasses decide with - * {@link #shouldClean(DataObject)}.
- * + * {@link #shouldClean(DataObject)}.
+ *

* Subclasses can react after an instance is removed by overriding {@link #postRemove(InstanceIdentifier)} - * @param The type that will be removed + * + * @param + * The type that will be removed */ - private abstract class AbstractCleaner implements Runnable { + private abstract class AbstractCleaner implements Runnable { final InstanceIdentifier removedIID; final Class tClass; final LogicalDatastoreType datastoreType; @@ -450,30 +480,32 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable if (shouldClean(optional.get())) { if (mdsalUtils.delete(datastoreType, tIID)) { postRemove(tIID); - } - else { + } else { removeFailed(tIID); } } } - } - else { - logger.error("Expected to find InstanceIdentifier<{}> but was not found: {}", - tClass.getSimpleName(), removedIID); + } else { + logger.error("Expected to find InstanceIdentifier<{}> but was not found: {}", tClass.getSimpleName(), + removedIID); } } /** * If returns true the object will be removed from the datastore - * @param object The object that might be removed. + * + * @param object + * The object that might be removed. * @return true if it should be removed. */ abstract boolean shouldClean(final T object); /** * Called after an instance is removed. - * @param tIID the InstanceIdentifier of the removed object + * + * @param tIID + * the InstanceIdentifier of the removed object */ void postRemove(InstanceIdentifier tIID) { @@ -548,6 +580,7 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable updateCcapMaps(ccap); logger.info("Created CCAP: {}/{} : {}", iid, ccap, message); logger.info("Created CCAP: {} : {}", iid, message); + connectionBuilder.setConnected(true).setError(Collections.emptyList()); } else { logger.error("Create CCAP Failed: {} : {}", iid, message); @@ -555,6 +588,10 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable connectionBuilder.setConnected(false).setError(Collections.singletonList(message)); } + //register rpc + logger.info("Registering CCAP Routed RPC Path..."); + rpcRegistration.registerPath(CcapContext.class, iid); + Optional optionalCcap = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, iid); final CcapBuilder responseCcapBuilder; @@ -594,6 +631,10 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable final Ccap originalCcap = originalCcaps.get(entry.getKey()); //final Ccap updatedCcap = entry.getValue(); + //register rpc + logger.info("Registering CCAP Routed RPC Path..."); + rpcRegistration.registerPath(CcapContext.class, entry.getKey()); + // restore the original data updateQueue.add(entry.getKey()); mdsalUtils.put(LogicalDatastoreType.CONFIGURATION, entry.getKey(), originalCcap); @@ -609,6 +650,10 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable final Ccap nukedCcap = originalCcaps.get(iid); removeCcapFromAllMaps(nukedCcap); + //unregister ccap rpc path + logger.info("Un-Registering CCAP Routed RPC Path..."); + rpcRegistration.unregisterPath(CcapContext.class, iid); + mdsalUtils.delete(LogicalDatastoreType.OPERATIONAL, iid); // clean up ccaps level if it is now empty @@ -660,6 +705,14 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable final String newGatePathStr = makeGatePathString(gateIID); + // if a new app comes along add RPC registration + final InstanceIdentifier appIID = gateIID.firstIdentifierOf(App.class); + // TBD verify if App ID exists first + + //register appID RPC path + logger.info("Registering App Routed RPC Path..."); + rpcRegistration.registerPath(AppContext.class, appIID); + final InstanceIdentifier subscriberIID = gateIID.firstIdentifierOf(Subscriber.class); final SubscriberKey subscriberKey = InstanceIdentifier.keyOf(subscriberIID); final InetAddress subscriberAddr = getInetAddress(subscriberKey.getSubscriberId()); @@ -683,8 +736,8 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable final ServiceClassName scn = newGate.getTrafficProfile().getServiceClassName(); final ServiceFlowDirection scnDirection = findScnOnCcap(scn, ccap); if (scnDirection == null) { - final String msg = String.format("SCN %s not found on CCAP %s for %s", - scn, ccap.getCcapId(), newGatePathStr); + final String msg = + String.format("SCN %s not found on CCAP %s for %s", scn, ccap.getCcapId(), newGatePathStr); logger.error(msg); saveGateError(gateIID, newGatePathStr, msg); continue; @@ -692,14 +745,16 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable final PCMMService pcmmService = pcmmServiceMap.get(ccap.getCcapId()); if (pcmmService == null) { - final String msg = String.format("Unable to locate PCMM Service for CCAP: %s ; with subscriber: %s", - ccap, subscriberKey.getSubscriberId()); + final String msg = + String.format("Unable to locate PCMM Service for CCAP: %s ; with subscriber: %s", ccap, + subscriberKey.getSubscriberId()); logger.error(msg); saveGateError(gateIID, newGatePathStr, msg); continue; } - PCMMService.GateSetStatus status = pcmmService.sendGateSet(newGatePathStr, subscriberAddr, newGate, scnDirection); + PCMMService.GateSendStatus status = + pcmmService.sendGateSet(newGatePathStr, subscriberAddr, newGate, scnDirection); if (status.didSucceed()) { gateMap.put(newGatePathStr, newGate); gateCcapMap.put(newGatePathStr, ccap.getCcapId()); @@ -709,9 +764,34 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable .setGatePath(newGatePathStr) .setCcapId(ccap.getCcapId()) .setCopsGateId(status.getCopsGateId()) - .setCopsState(status.didSucceed() ? "success" : "failure"); + .setCopsGateState("") + .setTimestamp(getNowTimeStamp()) + .setCopsGateTimeInfo("") + .setCopsGateUsageInfo("") + .setTimestamp(getNowTimeStamp()); + if (!status.didSucceed()) { gateBuilder.setError(Collections.singletonList(status.getMessage())); + } else { + PCMMService.GateSendStatus infoStatus = pcmmService.sendGateInfo(newGatePathStr); + + if (infoStatus.didSucceed()) { + gateBuilder.setCopsGateState( + infoStatus.getCopsGateState() + "/" + infoStatus.getCopsGateStateReason()) + .setCopsGateTimeInfo(infoStatus.getCopsGateTimeInfo()) + .setCopsGateUsageInfo(infoStatus.getCopsGateUsageInfo()); + } else { + List errors = new ArrayList<>(2); + + // Keep GateSetErrors + if (gateBuilder.getError() != null) { + errors.addAll(gateBuilder.getError()); + } + + errors.add(infoStatus.getMessage()); + gateBuilder.setError(errors); + } + } Gate operationalGate = gateBuilder.build(); @@ -731,9 +811,9 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable gateBuilder.setGateId(InstanceIdentifier.keyOf(gateIID).getGateId()) .setGatePath(gatePathStr) .setCopsGateId("") - .setCopsState("N/A"); + .setCopsGateState("N/A"); - gateBuilder.setError(Collections.singletonList(error)); + gateBuilder.setError(Collections.singletonList(error)); Gate operationalGate = gateBuilder.build(); @@ -782,21 +862,21 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable final String gatePathStr = makeGatePathString(removedGateIID); - if (gateMap.containsKey(gatePathStr)) { - final Gate thisGate = gateMap.remove(gatePathStr); - final String gateId = thisGate.getGateId(); - final String ccapId = gateCcapMap.remove(gatePathStr); - final Ccap thisCcap = ccapMap.get(ccapId); - final PCMMService service = pcmmServiceMap.get(thisCcap.getCcapId()); - if (service != null) { - service.sendGateDelete(gatePathStr); - logger.info("onDataChanged(): removed QoS gate {} for {}/{}/{}: ", gateId, ccapId, gatePathStr, - thisGate); - } else { - logger.warn( - "Unable to send to locate PCMMService to send gate delete message with CCAP - " + thisCcap); - } + if (gateMap.containsKey(gatePathStr)) { + final Gate thisGate = gateMap.remove(gatePathStr); + final String gateId = thisGate.getGateId(); + final String ccapId = gateCcapMap.remove(gatePathStr); + final Ccap thisCcap = ccapMap.get(ccapId); + final PCMMService service = pcmmServiceMap.get(thisCcap.getCcapId()); + if (service != null) { + service.sendGateDelete(gatePathStr); + logger.info("onDataChanged(): removed QoS gate {} for {}/{}/{}: ", gateId, ccapId, gatePathStr, + thisGate); + } else { + logger.warn("Unable to send to locate PCMMService to send gate delete message with CCAP - " + + thisCcap); } + } } @@ -812,10 +892,431 @@ public class PacketcableProvider implements BindingAwareProvider, AutoCloseable final GateKey gateKey = InstanceIdentifier.keyOf(iid); - return appKey.getAppId() - + "/" + subscriberKey.getSubscriberId() - + "/" + gateKey.getGateId(); + return appKey.getAppId() + "/" + subscriberKey.getSubscriberId() + "/" + gateKey.getGateId(); + } + } + + + @Override + public Future> ccapSetConnection(CcapSetConnectionInput input) { + // TODO refactor this method into smaller parts + + InstanceIdentifier ccapIid = (InstanceIdentifier) input.getCcapId(); + List outputError = new ArrayList(); + String rpcResponse = null; + Boolean inputIsConnected = input.getConnection().isConnected(); + Boolean effectiveIsConnected = null; + String ccapId = input.getCcapId().firstIdentifierOf(Ccap.class).firstKeyOf(Ccap.class).getCcapId(); + PCMMService pcmmService = pcmmServiceMap.get(ccapId); + + if (!inputIsConnected) { + // set connected false + if (pcmmService.getPcmmPdpSocket()) { + outputError.add(ccapId + ": CCAP COPS socket is already closed"); + effectiveIsConnected = false; + } else { + //if (!pcmmService.getPcmmCcapClientIsConnected()) { + outputError.add(ccapId + ": CCAP client is disconnected with error: " + + pcmmService.getPcmmCcapClientConnectErrMsg()); + //} + pcmmService.ccapClient.disconnect(); + effectiveIsConnected = false; + } + } else { + // set connected true + if (!pcmmService.getPcmmPdpSocket() && pcmmService.getPcmmCcapClientIsConnected()) { + outputError.add(ccapId + ": CCAP COPS socket is already open"); + outputError.add(ccapId + ": CCAP client is connected"); + effectiveIsConnected = true; + } else { + if (pcmmService.getPcmmCcapClientIsConnected()) { + pcmmService.ccapClient.disconnect(); + } + pcmmService.ccapClient.connect(); + if (pcmmService.getPcmmCcapClientIsConnected()) { + effectiveIsConnected = true; + outputError.add(ccapId + ": CCAP client is connected"); + } else { + effectiveIsConnected = false; + outputError.add(ccapId + ": CCAP client is disconnected with error: " + + pcmmService.getPcmmCcapClientConnectErrMsg()); + } + } + } + + DateAndTime connectionDateAndTime = getNowTimeStamp(); + org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.set.connection.output.ccap.ConnectionBuilder + connectionRpcOutput = + new org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.set.connection.output.ccap.ConnectionBuilder() + .setConnected(effectiveIsConnected) + .setError(outputError) + .setTimestamp(connectionDateAndTime); + + org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.set.connection.output.CcapBuilder ccapRpcOutput = + new org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.set.connection.output.CcapBuilder().setCcapId( + ccapId).setConnection(connectionRpcOutput.build()); + + + ConnectionBuilder connectionOps = new ConnectionBuilder().setConnected(effectiveIsConnected) + .setError(outputError) + .setTimestamp(connectionDateAndTime); + + CcapBuilder responseCcapBuilder = new CcapBuilder().setCcapId(ccapId).setConnection(connectionOps.build()); + + + mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, ccapIid, responseCcapBuilder.build()); + + + DateAndTime rpcDateAndTime = getNowTimeStamp(); + rpcResponse = ccapId + ": CCAP set complete"; + CcapSetConnectionOutputBuilder outputBuilder = + new CcapSetConnectionOutputBuilder().setCcap(ccapRpcOutput.build()) + .setResponse(rpcResponse) + .setTimestamp(rpcDateAndTime); + + return Futures.immediateFuture(RpcResultBuilder.success(outputBuilder.build()).build()); + } + + + + @Override + public Future> ccapPollConnection(CcapPollConnectionInput input) { + // TODO refactor this method into smaller parts + + InstanceIdentifier ccapIid = (InstanceIdentifier) input.getCcapId(); + List outputError = new ArrayList(); + + String ccapId = input.getCcapId().firstIdentifierOf(Ccap.class).firstKeyOf(Ccap.class).getCcapId(); + PCMMService pcmmService = pcmmServiceMap.get(ccapId); + Boolean effectiveIsConnected = true; + String response = null; + org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.poll.connection.output.ccap.ConnectionBuilder + connectionRpcOutput = + new org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.poll.connection.output.ccap.ConnectionBuilder(); + + if (pcmmService != null) { + if (pcmmService.getPcmmPdpSocket()) { + outputError.add(ccapId + ": CCAP Cops socket is closed"); + if (!pcmmService.getPcmmCcapClientIsConnected()) { + outputError.add(ccapId + ": CCAP client is disconnected with error: " + + pcmmService.getPcmmCcapClientConnectErrMsg()); + } + effectiveIsConnected = false; + } else { + //outputError.add(String.format(ccapId+": CCAP Cops socket is open")); + if (!pcmmService.getPcmmCcapClientIsConnected()) { + outputError.add(ccapId + ": CCAP client is disconnected with error: " + + pcmmService.getPcmmCcapClientConnectErrMsg()); + effectiveIsConnected = false; + } else { + outputError.add(ccapId + ": CCAP client is connected"); + } + } + DateAndTime connectionDateAndTime = getNowTimeStamp(); + + + ConnectionBuilder connectionOps = new ConnectionBuilder().setConnected(effectiveIsConnected) + .setError(outputError) + .setTimestamp(connectionDateAndTime); + + CcapBuilder responseCcapBuilder = new CcapBuilder().setCcapId(ccapId).setConnection(connectionOps.build()); + + connectionRpcOutput = + new org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.poll.connection.output.ccap.ConnectionBuilder() + .setConnected(effectiveIsConnected) + .setError(outputError) + .setTimestamp(connectionDateAndTime); + + mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, ccapIid, responseCcapBuilder.build()); + response = ccapId + ": CCAP poll complete"; + } else { + //pcmmService is null, do not poll + response = ccapId + ": CCAP connection null; no poll performed"; + } + + DateAndTime rpcDateAndTime = getNowTimeStamp(); + + org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.poll.connection.output.CcapBuilder ccapRpcOutput = + new org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccap.poll.connection.output.CcapBuilder().setCcapId( + ccapId).setConnection(connectionRpcOutput.build()); + + CcapPollConnectionOutputBuilder outputBuilder = + new CcapPollConnectionOutputBuilder().setCcap(ccapRpcOutput.build()) + .setResponse(response) + .setTimestamp(rpcDateAndTime); + + return Futures.immediateFuture(RpcResultBuilder.success(outputBuilder.build()).build()); + } + + + + private App readAppFromOperationalDatastore(InstanceIdentifier appIid) { + Optional optionalApp = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, appIid); + AppBuilder thisAppBuilder = new AppBuilder(optionalApp.get()); + App thisApp = thisAppBuilder.build(); + logger.info("readAppFromConfigDatastore() retrived App: " + thisApp.getAppId()); + return thisApp; + } + + private Gate readGateFromOperationalDatastore(InstanceIdentifier gateIid) { + Optional optionalGate = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, gateIid); + if (optionalGate.isPresent()) { + GateBuilder gateBuilder = new GateBuilder(optionalGate.get()); + Gate thisGate = gateBuilder.build(); + return thisGate; + } else { + return null; + } + } + + private Subscriber readSubscriberFromOperationalDatastore(InstanceIdentifier subscriberIid) { + Optional optionalSubscriber = mdsalUtils.read(LogicalDatastoreType.OPERATIONAL, subscriberIid); + if (optionalSubscriber.isPresent()) { + SubscriberBuilder subscriberBuilder = new SubscriberBuilder(optionalSubscriber.get()); + Subscriber thisSubscriber = subscriberBuilder.build(); + return thisSubscriber; + } else { + return null; } } + + + @Override + public Future> qosPollGates(QosPollGatesInput input) { + // TODO refactor this method into smaller parts + + InstanceIdentifier appIid = (InstanceIdentifier) input.getAppId(); + //logger.info("qospollgates appIid : "+appIid.toString()); + App app = readAppFromOperationalDatastore(appIid); + //logger.info("qospollgates app : "+app.toString()); + AppKey appKey = InstanceIdentifier.keyOf(appIid); + String inputSubscriberId = input.getSubscriberId(); + String inputGateId = input.getGateId(); + List gateOutputError = Collections.emptyList(); + String subscriberId = null; + String gateId = null; + String ccapId = null; + String gatePathStr = null; + String opsCopsGateId = null; + Gate opsGate = null; + + String rpcResponse = null; + + org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.qos.poll.gates.output.GateBuilder gateOutputBuilder = + new org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.qos.poll.gates.output.GateBuilder(); + + GateBuilder gateBuilder = new GateBuilder(); + + if (inputSubscriberId != null) { + if (inputGateId != null) { + //Subscriber Id and Gate Id provided, only one gate to be poolled + + //generate the gateiid + InstanceIdentifier gateIid = appIid.builder() + .child(Subscribers.class) + .child(Subscriber.class, new SubscriberKey(inputSubscriberId)) + .child(Gates.class) + .child(Gate.class, new GateKey(inputGateId)) + .build(); + + + opsGate = readGateFromOperationalDatastore(gateIid); + + //does the gate exists in the Operational DS? + if (opsGate == null) { + gatePathStr = appKey.getAppId() + "/" + inputSubscriberId + "/" + inputGateId; + rpcResponse = gatePathStr + ": gate does not exist in the system; gate poll not performed"; + } else { + opsCopsGateId = opsGate.getCopsGateId(); + gatePathStr = opsGate.getGatePath(); + + if ((!Objects.equals(opsCopsGateId, "")) && (!Objects.equals(opsCopsGateId, null))) { + ccapId = findCcapForSubscriberId(getInetAddress(inputSubscriberId)).getCcapId(); + PCMMService pcmmService = pcmmServiceMap.get(ccapId); + //is the CCAP socket open? + if (!pcmmService.getPcmmPdpSocket() && pcmmService.getPcmmCcapClientIsConnected()) { + PCMMService.GateSendStatus status = pcmmService.sendGateInfo(gatePathStr); + DateAndTime gateDateAndTime = getNowTimeStamp(); + //logger.info("qospollgates Gate Status : GateID/"+status.getCopsGateId()); + //logger.info("qospollgates Gate Status : Message/"+status.getMessage()); + //logger.info("qospollgates Gate Status : DidSucceed/"+status.didSucceed()); + gateOutputError = Collections.singletonList(status.getMessage()); + + gateOutputBuilder.setGatePath(gatePathStr) + .setCcapId(ccapId) + .setCopsGateState(status.getCopsGateState() + "/" + status.getCopsGateStateReason()) + .setCopsGateTimeInfo(status.getCopsGateTimeInfo()) + .setCopsGateUsageInfo(status.getCopsGateUsageInfo()) + .setCopsGateId(status.getCopsGateId()) + .setError(gateOutputError) + .setTimestamp(gateDateAndTime); + + gateBuilder.setGateId(inputGateId) + .setGatePath(gatePathStr) + .setCcapId(ccapId) + .setCopsGateState(status.getCopsGateState() + "/" + status.getCopsGateStateReason()) + .setCopsGateTimeInfo(status.getCopsGateTimeInfo()) + .setCopsGateUsageInfo(status.getCopsGateUsageInfo()) + .setCopsGateId(status.getCopsGateId()) + .setError(gateOutputError) + .setTimestamp(gateDateAndTime); + + mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIid, gateBuilder.build()); + rpcResponse = gatePathStr + ": gate poll complete"; + } else { + rpcResponse = + ccapId + ": CCAP socket is down or client disconnected; gate poll not performed"; + } + } else { + rpcResponse = gatePathStr + ": gate not active; gate poll not performed"; + } + } + } else { + //inputGateId is null; pool all gates for the subscriber if the sub exists + + //generate active subIid + InstanceIdentifier subIid = appIid.builder() + .child(Subscribers.class) + .child(Subscriber.class, new SubscriberKey(inputSubscriberId)) + .build(); + //does the subscriber provided exists in the Operational Datastore? + Subscriber sub = readSubscriberFromOperationalDatastore(subIid); + if (sub != null) { + //If Subscriber exsits poll all gates for the subscriber + subscriberId = sub.getSubscriberId(); + List gateList = sub.getGates().getGate(); + for (Gate gate : gateList) { + //generate active gateIid + gateId = gate.getGateId(); + InstanceIdentifier gateIid = + subIid.builder().child(Gates.class).child(Gate.class, new GateKey(gateId)).build(); + + opsGate = readGateFromOperationalDatastore(gateIid); + opsCopsGateId = opsGate.getCopsGateId(); + //generate active gatePathStr + gatePathStr = appKey.getAppId() + "/" + subscriberId + "/" + gateId; + + if ((!Objects.equals(opsCopsGateId, "")) && (!Objects.equals(opsCopsGateId, null))) { + ccapId = findCcapForSubscriberId(getInetAddress(subscriberId)).getCcapId(); + PCMMService pcmmService = pcmmServiceMap.get(ccapId); + //is the CCAP socket open? + if (!pcmmService.getPcmmPdpSocket() && pcmmService.getPcmmCcapClientIsConnected()) { + PCMMService.GateSendStatus status = pcmmService.sendGateInfo(gatePathStr); + DateAndTime gateDateAndTime = getNowTimeStamp(); + + gateBuilder.setGateId(gateId) + .setGatePath(gatePathStr) + .setCcapId(ccapId) + .setCopsGateState( + status.getCopsGateState() + "/" + status.getCopsGateStateReason()) + .setCopsGateTimeInfo(status.getCopsGateTimeInfo()) + .setCopsGateUsageInfo(status.getCopsGateUsageInfo()) + .setCopsGateId(status.getCopsGateId()) + .setError(gateOutputError) + .setTimestamp(gateDateAndTime); + + mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIid, gateBuilder.build()); + } else { + logger.info( + "qospollgates: {}: CCAP Cops socket is down or client disconnected; gate poll not performed", + ccapId); + } + } else { + //TODO define what happens if a gate is not active.. is nothing ok? + logger.info("qospollgates: {}: gate not active; gate poll not performed", gatePathStr); + } + } //for + rpcResponse = inputSubscriberId + "/: subscriber subtree poll in progress"; + } else { + rpcResponse = + inputSubscriberId + "/: subscriber is not defined in the system, gate poll not performed"; + } + } + } //inputSubId if + else { + // inputSubId is null + if (inputGateId != null) { + gatePathStr = appKey.getAppId() + "/" + inputSubscriberId + "/" + inputGateId; + rpcResponse = gatePathStr + ": Subscriber ID not provided; gate poll not performed"; + } else { + //poll all gates for the appId + + Subscribers subs = app.getSubscribers(); + + logger.info("qospollgates subscribers: " + subs.toString()); + + List subList = subs.getSubscriber(); + logger.info("qospollgates subList: " + subList.toString()); + for (Subscriber sub : subList) { + + //generate active subIid + subscriberId = sub.getSubscriberId(); + InstanceIdentifier subIid = appIid.builder() + .child(Subscribers.class) + .child(Subscriber.class, new SubscriberKey(subscriberId)) + .build(); + + List gateList = sub.getGates().getGate(); + for (Gate gate : gateList) { + //logger.info("qospollgates active gate: "+gate); + + //generate active gateIid + gateId = gate.getGateId(); + InstanceIdentifier gateIid = + subIid.builder().child(Gates.class).child(Gate.class, new GateKey(gateId)).build(); + + opsGate = readGateFromOperationalDatastore(gateIid); + opsCopsGateId = opsGate.getCopsGateId(); + //generate active gatePathStr + gatePathStr = appKey.getAppId() + "/" + subscriberId + "/" + gateId; + if ((!Objects.equals(opsCopsGateId, "")) && (!Objects.equals(opsCopsGateId, null))) { + ccapId = findCcapForSubscriberId(getInetAddress(subscriberId)).getCcapId(); + PCMMService pcmmService = pcmmServiceMap.get(ccapId); + //is the CCAP socket open? + if (!pcmmService.getPcmmPdpSocket() && pcmmService.getPcmmCcapClientIsConnected()) { + PCMMService.GateSendStatus status = pcmmService.sendGateInfo(gatePathStr); + DateAndTime gateDateAndTime = getNowTimeStamp(); + gateOutputError = Collections.singletonList(status.getMessage()); + + + gateBuilder.setGateId(gateId) + .setGatePath(gatePathStr) + .setCcapId(ccapId) + .setCopsGateState( + status.getCopsGateState() + "/" + status.getCopsGateStateReason()) + .setCopsGateTimeInfo(status.getCopsGateTimeInfo()) + .setCopsGateUsageInfo(status.getCopsGateUsageInfo()) + .setCopsGateId(status.getCopsGateId()) + .setError(gateOutputError) + .setTimestamp(gateDateAndTime); + + mdsalUtils.put(LogicalDatastoreType.OPERATIONAL, gateIid, gateBuilder.build()); + } else { + logger.info( + "qospollgates: {}: CCAP socket is down or client disconnected; gate poll not performed", + ccapId); + } + } else { + //TODO define what happens if a gate is not active.. is nothing ok + logger.info("qospollgates: {}: gate not active; gate poll not performed", gatePathStr); + } + } + } + rpcResponse = appKey.getAppId() + "/: gate subtree poll in progress"; + } + } + + DateAndTime rpcDateAndTime = getNowTimeStamp(); + + QosPollGatesOutputBuilder outputBuilder = new QosPollGatesOutputBuilder().setTimestamp(rpcDateAndTime) + .setResponse(rpcResponse) + .setGate(gateOutputBuilder.build()); + return Futures.immediateFuture(RpcResultBuilder.success(outputBuilder.build()).build()); + } + + private DateAndTime getNowTimeStamp() { + DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); + return new DateAndTime(dateFormat.format(new Date())); + } } diff --git a/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java b/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java index 650ae33..4d940e8 100644 --- a/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java +++ b/packetcable-policy-server/src/test/java/org/opendaylight/controller/packetcable/provider/PCMMServiceTest.java @@ -5,6 +5,9 @@ package org.opendaylight.controller.packetcable.provider; import static junit.framework.TestCase.assertEquals; + +import static org.hamcrest.CoreMatchers.startsWith; +import static org.hamcrest.MatcherAssert.assertThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -43,10 +46,10 @@ import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gate.spec import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate; import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.traffic.profile.TrafficProfile; import org.pcmm.PCMMPdpAgent; -import org.pcmm.gates.IGateSpec.Direction; import org.pcmm.gates.IPCMMGate; import org.pcmm.rcd.IPCMMClient; import org.pcmm.rcd.impl.CMTS; +import org.pcmm.rcd.impl.CMTSConfig; import org.umu.cops.stack.COPSClientSI; import org.umu.cops.stack.COPSContext; import org.umu.cops.stack.COPSContext.RType; @@ -120,33 +123,33 @@ public class PCMMServiceTest { public void setup() throws IOException { srcAddr = new Ipv4Address("10.10.10.0"); dstAddr = new Ipv4Address("10.32.99.99"); + invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99}); if (realCmts) { cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)172}); - invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99}); + // Use me when testing against a CMTS or emulator not running in the same JVM cmtsAddr = new Ipv4Address("10.32.10.3"); ccap = makeCcapObj(PCMMPdpAgent.WELL_KNOWN_PDP_PORT, cmtsAddr, ccapId); } else { cmAddrInet = InetAddress.getByAddress(new byte[] {10, 32, 110, (byte)180}); - invalidCmAddrInet = InetAddress.getByAddress(new byte[] {99, 99, 99, 99}); // Use me for automated testing and the CMTS emulator running in the same JVM cmtsAddr = new Ipv4Address("127.0.0.1"); - final Set upGate = new HashSet<>(); - upGate.add("extrm_up"); - final Set dnGate = new HashSet<>(); - dnGate.add("extrm_dn"); - final Map> gates = new HashMap<>(); - gates.put(Direction.UPSTREAM, upGate); - gates.put(Direction.DOWNSTREAM, dnGate); + final Set upSCN = new HashSet<>(); + upSCN.add("extrm_up"); + final Set dnSCN = new HashSet<>(); + dnSCN.add("extrm_dn"); final Map cmStatus = new HashMap<>(); cmStatus.put(cmAddrInet.getHostAddress(), true); cmStatus.put(invalidCmAddrInet.getHostAddress(), false); - icmts = new CMTS(gates, cmStatus); + + CMTSConfig config = new CMTSConfig(0, (short)4, upSCN, dnSCN, cmStatus); + + icmts = new CMTS(config); icmts.startServer(); ccap = makeCcapObj(icmts.getPort(), cmtsAddr, ccapId); @@ -250,7 +253,7 @@ public class PCMMServiceTest { public void testAddAndRemoveInvalidCmAddrUpGate() throws Exception { // TODO - fix cmts emulator final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath - + " returned error - Error Code: 13 Error Subcode : 0 Invalid SubscriberID"; + + " returned error - Error Code: 13 Subcode: 0 Invalid SubscriberID"; addInvalidGate(service, "extrm_up", srcAddr, dstAddr, ServiceFlowDirection.Us, invalidCmAddrInet, gatePath, expectedMsgStart); } @@ -258,7 +261,7 @@ public class PCMMServiceTest { @Test public void testAddInvalidScnUpGate() throws Exception { final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath - + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name"; + + " returned error - Error Code: 11 Subcode: 0 Undefined Service Class Name"; addInvalidGate(service, "extrm_up_invalid", srcAddr, dstAddr, ServiceFlowDirection.Us, cmAddrInet, gatePath, expectedMsgStart); } @@ -266,7 +269,7 @@ public class PCMMServiceTest { @Test public void testAddInvalidScnDownGate() throws Exception { final String expectedMsgStart = "404 Not Found - sendGateSet for " + ccapId + '/' + gatePath - + " returned error - Error Code: 11 Error Subcode : 0 Undefined Service Class Name"; + + " returned error - Error Code: 11 Subcode: 0 Undefined Service Class Name"; addInvalidGate(service, "extrm_dn_invalid", srcAddr, dstAddr, ServiceFlowDirection.Ds, cmAddrInet, gatePath, expectedMsgStart); } @@ -362,10 +365,11 @@ public class PCMMServiceTest { // Assert.assertNotNull(gateSetMsg); // Assert.assertTrue(gateSetMsg, gateSetMsg.startsWith(expGateSetMsgStart)); - // TODO update this method for the new GateSetStatus object - PCMMService.GateSetStatus status = service.sendGateSet(gatePath, cmAddrInet, gate, direction); + // TODO update this method for the new GateSendStatus object + PCMMService.GateSendStatus status = service.sendGateSet(gatePath, cmAddrInet, gate, direction); Assert.assertNotNull(status); - Assert.assertTrue(status.getMessage().startsWith(expGateSetMsgStart)); + assertThat(status.getMessage(), startsWith(expGateSetMsgStart)); + // TODO - add validation to the PCMMGateReq contained within the map if (status.didSucceed()) {