Expanded CMTS emulator to accept at least one type of gate request. Future work will...
[packetcable.git] / packetcable-policy-server / src / main / java / org / opendaylight / controller / packetcable / provider / PCMMService.java
1 package org.opendaylight.controller.packetcable.provider;
2
3 import com.google.common.collect.Maps;
4 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
5 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.Ipv4Address;
6 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
7 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.ServiceClassName;
8 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.ServiceFlowDirection;
9 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.ccap.Ccaps;
10 import org.opendaylight.yang.gen.v1.urn.packetcable.rev150327.pcmm.qos.gates.apps.subs.Gates;
11 import org.pcmm.PCMMPdpAgent;
12 import org.pcmm.PCMMPdpDataProcess;
13 import org.pcmm.PCMMPdpMsgSender;
14 import org.pcmm.gates.impl.PCMMGateReq;
15 import org.slf4j.Logger;
16 import org.slf4j.LoggerFactory;
17 import org.umu.cops.prpdp.COPSPdpException;
18 import org.umu.cops.stack.COPSError;
19 import org.umu.cops.stack.COPSError.ErrorTypes;
20 import org.umu.cops.stack.COPSException;
21
22 import javax.annotation.concurrent.ThreadSafe;
23 import java.io.IOException;
24 import java.net.InetAddress;
25 import java.util.Map;
26
27 /**
28  * Class responsible for managing the gates for a single CCAP.
29  */
30 @ThreadSafe
31 public class PCMMService {
32         private Logger logger = LoggerFactory.getLogger(PCMMService.class);
33
34         private final Ccaps ccap;
35         private final IpAddress ipAddr;
36         private final PortNumber portNum;
37         protected final CcapClient ccapClient;
38         protected Map<String, PCMMGateReq> gateRequests = Maps.newConcurrentMap();
39
40         private final short clientType;
41
42         public PCMMService(final short clientType, final Ccaps ccap) {
43                 this.clientType = clientType;
44                 this.ccap = ccap;
45                 ipAddr = ccap.getConnection().getIpAddress();
46                 portNum = ccap.getConnection().getPort();
47                 ccapClient = new CcapClient(ipAddr, portNum);
48                 logger.info("Attempting to add CCAP with ID {} @ {}:{}", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(), portNum.getValue());
49         }
50
51         public void disconect() {
52                 ccapClient.disconnect();
53         }
54
55         // TODO - try and change the return to something other than a String to be parsed to determine success
56         public String addCcap() {
57                 ccapClient.connect();
58                 if (ccapClient.isConnected) {
59                         logger.info("Connected to CCAP with ID - " + ccap.getCcapId());
60                         return String.format("200 OK - CCAP %s connected @ %s:%d", ccap.getCcapId(),
61                                         ipAddr.getIpv4Address().getValue(), portNum.getValue());
62                 } else {
63                         return String.format("404 Not Found - CCAP %s failed to connect @ %s:%d - %s",
64                                         ccap.getCcapId(),
65                                         ipAddr.getIpv4Address().getValue(), portNum.getValue(), ccapClient.errMessage);
66                 }
67         }
68
69         // TODO - Consider creating an object to return that contains a success flag, message, and gate ID or gate object
70         // TODO FIXME - the gate appears to be getting set as per restconf but I am not seeing the proper logging occurring
71         public String sendGateSet(final String gatePathStr, final InetAddress subId, final Gates qosGate,
72                                                           final ServiceFlowDirection scnDir) {
73                 logger.info("Sending gate to CCAP with ID - " + ccap.getCcapId());
74                 // assemble the gate request for this subId
75                 final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
76                 gateBuilder.build(ccap.getAmId());
77                 gateBuilder.build(subId);
78                 // force gateSpec.Direction to align with SCN direction
79                 final ServiceClassName scn = qosGate.getTrafficProfile().getServiceClassName();
80                 if (scn != null) {
81                         gateBuilder.build(qosGate.getGateSpec(), scnDir);
82                 } else {
83                         // not an SCN gate
84                         gateBuilder.build(qosGate.getGateSpec(), null);
85                 }
86                 gateBuilder.build(qosGate.getTrafficProfile());
87
88                 // pick a classifier type (only one for now)
89                 if (qosGate.getClassifier() != null) {
90                         gateBuilder.build(qosGate.getClassifier());
91                 } else if (qosGate.getExtClassifier() != null) {
92                         gateBuilder.build(qosGate.getExtClassifier());
93                 } else if (qosGate.getIpv6Classifier() != null) {
94                         gateBuilder.build(qosGate.getIpv6Classifier());
95                 }
96                 // assemble the final gate request
97                 final PCMMGateReq gateReq = gateBuilder.getGateReq();
98
99                 // and remember it
100                 gateRequests.put(gatePathStr, gateReq);
101                 // and send it to the CCAP
102                 ccapClient.sendGateSet(gateReq);
103                 // and wait for the COPS response to complete processing gate request
104                 try {
105                         // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
106                         // TODO - handle this synchronization.
107                         // TODO - if not changing this, may want to make this timeout configurable
108                         synchronized(gateReq) {
109                                 logger.info("Waiting 1000ms for gate request to be updated");
110                                 gateReq.wait(1000);
111                                 logger.debug("Gate request error - " + gateReq.getError());
112                                 logger.debug("Gate request ID - " + gateReq.getGateID());
113                         }
114                 } catch (Exception e) {
115                         logger.error("PCMMService: sendGateSet(): gate response timeout exceeded for "
116                                         + gatePathStr + '/' + gateReq, e);
117                         return String.format("408 Request Timeout - gate response timeout exceeded for %s/%s",
118                                         ccap.getCcapId(), gatePathStr);
119                 }
120                 if (gateReq.getError() != null) {
121                         logger.error("PCMMService: sendGateSet(): returned error: {}",
122                                         gateReq.getError().toString());
123                         return String.format("404 Not Found - sendGateSet for %s/%s returned error - %s",
124                                         ccap.getCcapId(), gatePathStr, gateReq.getError().toString());
125                 } else {
126                         if (gateReq.getGateID() != null) {
127                                 logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
128                                                 gateReq.getGateID().getGateID()));
129                                 return String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
130                                                 ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID());
131                         } else {
132                                 logger.info("PCMMService: sendGateSet(): no gateId returned:");
133                                 return String.format("404 Not Found - sendGateSet for %s/%s no gateId returned",
134                                                 ccap.getCcapId(), gatePathStr);
135                         }
136                 }
137         }
138
139         public Boolean sendGateDelete(final String gatePathStr) {
140                 logger.info("sendGateDelete() - " + ccap);
141                 // recover the original gate request
142                 final PCMMGateReq gateReq = gateRequests.remove(gatePathStr);
143                 if (gateReq != null) {
144                         ccapClient.sendGateDelete(gateReq);
145                         // and wait for the response to complete
146                         try {
147                                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
148                                 // TODO - handle this synchronization.
149                                 synchronized(gateReq) {
150                                         gateReq.wait(1000);
151                                 }
152                         } catch (InterruptedException e) {
153                                 logger.error("PCMMService: sendGateDelete(): gate response timeout exceeded for {}/{}",
154                                                 gatePathStr, gateReq);
155                         }
156                         if (gateReq.getError() != null) {
157                                 logger.warn("PCMMService: sendGateDelete(): returned error: {}", gateReq.getError().toString());
158                                 return false;
159                         } else {
160                                 if (gateReq.getGateID() != null) {
161                                         logger.info(String.format("PCMMService: sendGateDelete(): deleted GateId %08x: ", gateReq.getGateID().getGateID()));
162                                 } else {
163                                         logger.error("PCMMService: sendGateDelete(): deleted but no gateId returned");
164                                 }
165                                 return true;
166                         }
167                 } else {
168                         return false;
169                 }
170         }
171
172         private String getIpAddressStr(final IpAddress ipAddress) {
173                 final Ipv4Address ipv4 = ipAddress.getIpv4Address();
174                 if (ipv4 != null) {
175                         return ipv4.getValue();
176                 } else {
177                         return ipAddress.getIpv6Address().getValue();
178                 }
179         }
180
181         /**
182          * Used to interface with a CCAP (including CMTSs)
183          */
184         protected class CcapClient {
185                 public final PCMMPdpDataProcess pcmmProcess;
186                 public final PCMMPdpAgent pcmmPdp;
187
188                 private final String ipv4;
189                 private final Integer port;
190
191                 // Needs to be initialized in connect() method else would be final
192                 protected transient PCMMPdpMsgSender pcmmSender;
193
194                 private transient Boolean isConnected = false;
195                 private transient String errMessage = null;
196
197                 /**
198                  * Constructor
199                  * @param ccapIp - the IP of the CCAP to manage
200                  * @param portNum - the port number of the CCAP to manage
201                  */
202                 public CcapClient(final IpAddress ccapIp, final PortNumber portNum) {
203                         ipv4 = ccapIp.getIpv4Address().getValue();
204                         if (portNum != null)  port = portNum.getValue();
205                         else port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT;
206                         // TODO FIXME - if this object is not null, gate processing will not work correctly
207                         // TODO see - PCMMPdpReqStateMan#processReport() where the report type is success and the process is null
208                         //            pcmmProcess = new PCMMPdpDataProcess();
209                         pcmmProcess = null;
210                         pcmmPdp = new PCMMPdpAgent(clientType, ipv4, port, pcmmProcess);
211                 }
212
213                 /**
214                  * Starts the connection to the CCAP
215                  */
216                 public void connect( ) {
217                         logger.info("Attempting to connect to host: " + ipv4 + " port: " + port);
218                         try  {
219                                 pcmmPdp.connect();
220
221                                 // Cannot instantiate until after pcmmPdp.connect() is called as this is where the client handle is created
222                                 pcmmSender = new PCMMPdpMsgSender(clientType, pcmmPdp.getClientHandle(), pcmmPdp.getSocket());
223
224                                 isConnected = true;
225                         } catch (Exception e) {
226                                 isConnected = false;
227                                 logger.error("Failed to connect to host: " + ipv4 + " port: " + port, e);
228                                 errMessage = e.getMessage();
229                         }
230                 }
231
232                 public void disconnect() {
233                         logger.info("CcapClient: disconnect(): {}:{}", ipv4, port);
234                         try {
235                                 pcmmPdp.disconnect(pcmmPdp.getPepIdString(), new COPSError(ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA));
236                                 isConnected = false;
237                         } catch (COPSException | IOException e) {
238                                 logger.error("CcapClient: disconnect(): {}:{} FAILED: {}", ipv4, port, e.getMessage());
239                         }
240                 }
241
242                 // TODO - consider returning a new PCMMGateReq object or a future here instead of setting the ID on the old
243                 // TODO - request by reference which makes the code more convoluted thus making issues more difficult to track down.
244                 public Boolean sendGateSet(final PCMMGateReq gateReq) {
245                         logger.info("CcapClient: sendGateSet(): {}:{} => {}", ipv4, port, gateReq);
246                         try {
247                                 pcmmSender.sendGateSet(gateReq);
248
249                                 // TODO - determine if this is the correct place to perform this operation as this currently is the
250                                 // TODO - place where the gate ID can be set on the gateReq object
251                                 //                pcmmSender.handleGateReport(pcmmPdp.getSocket());
252                         } catch (COPSPdpException e) {
253                                 logger.error("CcapClient: sendGateSet(): {}:{} => {} FAILED:", ipv4, port, gateReq, e);
254                         }
255                         // and save it back to the gateRequest object for gate delete later
256                         gateReq.setGateID(pcmmSender.getGateID());
257
258                         // TODO - determine why this method is always returning true???
259                         return true;
260                 }
261
262                 public Boolean sendGateDelete(final PCMMGateReq gateReq) {
263                         logger.info("CcapClient: sendGateDelete(): {}:{} => {}", ipv4, port, gateReq);
264                         try {
265                                 pcmmSender.sendGateDelete(gateReq);
266                         } catch (COPSPdpException e) {
267                                 logger.error("CcapClient: sendGateDelete(): {}:{} => {} FAILED: {}", ipv4, port, gateReq, e.getMessage());
268                         }
269                         return true;
270                 }
271         }
272 }
273