Added GateInfo to Op Ds and RPCs to support gate update requests
[packetcable.git] / packetcable-policy-server / src / main / java / org / opendaylight / controller / packetcable / provider / PCMMService.java
1 /*
2  * Copyright (c) 2015 CableLabs and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.controller.packetcable.provider;
9
10 import com.google.common.collect.Maps;
11 import java.net.InetAddress;
12 import java.util.Map;
13 import javax.annotation.concurrent.ThreadSafe;
14 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.IpAddress;
15 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev100924.PortNumber;
16 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceClassName;
17 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ServiceFlowDirection;
18 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.ccaps.Ccap;
19 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151101.pcmm.qos.gates.apps.app.subscribers.subscriber.gates.Gate;
20 import org.pcmm.PCMMPdpAgent;
21 import org.pcmm.PCMMPdpDataProcess;
22 import org.pcmm.PCMMPdpMsgSender;
23 import org.pcmm.gates.IGateState;
24 import org.pcmm.gates.ITransactionID;
25 import org.pcmm.gates.impl.PCMMGateReq;
26 import org.pcmm.gates.impl.TransactionID;
27 import org.slf4j.Logger;
28 import org.slf4j.LoggerFactory;
29 import org.umu.cops.prpdp.COPSPdpException;
30 import org.umu.cops.stack.COPSError;
31 import org.umu.cops.stack.COPSError.ErrorTypes;
32
33 /**
34  * Class responsible for managing the gates for a single CCAP.
35  */
36 @ThreadSafe
37 public class PCMMService {
38     private final Logger logger = LoggerFactory.getLogger(PCMMService.class);
39
40     private final Ccap ccap;
41     private final IpAddress ipAddr;
42     private final PortNumber portNum;
43     protected final CcapClient ccapClient;
44     protected Map<String, PCMMGateReq> gateRequests = Maps.newConcurrentMap();
45
46     private final short clientType;
47
48     public PCMMService(final short clientType, final Ccap ccap) {
49         this.clientType = clientType;
50         this.ccap = ccap;
51         ipAddr = ccap.getConnection().getIpAddress();
52         portNum = ccap.getConnection().getPort();
53
54         ccapClient = new CcapClient(ipAddr, portNum);
55         logger.info("Attempting to add CCAP with ID {} @ {}:{}", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(),
56                 portNum.getValue());
57     }
58
59     public void disconect() {
60         ccapClient.disconnect();
61     }
62
63     // TODO - try and change the return to something other than a String to be parsed to determine success
64     public String addCcap() {
65         ccapClient.connect();
66         if (ccapClient.isConnected) {
67             logger.info("Connected to CCAP with ID - " + ccap.getCcapId());
68             return String
69                     .format("200 OK - CCAP %s connected @ %s:%d", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(),
70                             portNum.getValue());
71         } else {
72             return String.format("404 Not Found - CCAP %s failed to connect @ %s:%d - %s", ccap.getCcapId(),
73                     ipAddr.getIpv4Address().getValue(), portNum.getValue(), ccapClient.errMessage);
74         }
75     }
76
77     public class GateSendStatus {
78         private boolean didSucceed = false;
79         private String message = "";
80         private String copsGateId = "";
81         private String copsGateState = "";
82         private String copsGateStateReason = "";
83         private String copsGateTimeInfo = "";
84         private String copsGateUsageInfo = "";
85
86         public boolean didSucceed() {
87             return didSucceed;
88         }
89
90         void setDidSucceed(final boolean didSucceed) {
91             this.didSucceed = didSucceed;
92         }
93
94         public String getMessage() {
95             return message;
96         }
97
98         void setMessage(final String message) {
99             this.message = message;
100         }
101
102         public String getCopsGateId() {
103             return copsGateId;
104         }
105
106         void setCopsGateId(final String copsGateId) {
107             this.copsGateId = copsGateId;
108         }
109
110         public String getCopsGateState() {
111             return copsGateState;
112         }
113         void setCopsGateState(final String copsGateState) {
114             this.copsGateState = copsGateState;
115         }
116
117         public String getCopsGateStateReason() {
118             return copsGateStateReason;
119         }
120         void setCopsGateStateReason(final String copsGateStateReason) {
121             this.copsGateStateReason = copsGateStateReason;
122         }
123
124         public String getCopsGateTimeInfo() {
125             return copsGateTimeInfo;
126         }
127         void setCopsGateTimeInfo(final String copsGateTimeInfo) {
128             this.copsGateTimeInfo = copsGateTimeInfo;
129         }
130
131         public String getCopsGateUsageInfo() {
132             return copsGateUsageInfo;
133         }
134         void setCopsGateUsageInfo(final String copsGateUsageInfo) {
135             this.copsGateUsageInfo = copsGateUsageInfo;
136         }
137     }
138
139     public GateSendStatus sendGateSet(final String gatePathStr, final InetAddress subId, final Gate qosGate,
140             final ServiceFlowDirection scnDir) {
141
142         GateSendStatus status = new GateSendStatus();
143
144         logger.info("Sending gate to CCAP with ID - " + ccap.getCcapId());
145
146         // assemble the gate request for this subId
147         final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
148         gateBuilder.setAmId(ccap.getAmId());
149         gateBuilder.setSubscriberId(subId);
150
151         // force gateSpec.Direction to align with SCN direction
152         final ServiceClassName scn = qosGate.getTrafficProfile().getServiceClassName();
153         if (scn != null) {
154             gateBuilder.setGateSpec(qosGate.getGateSpec(), scnDir);
155         } else {
156             // not an SCN gate
157             gateBuilder.setGateSpec(qosGate.getGateSpec(), null);
158         }
159         gateBuilder.setTrafficProfile(qosGate.getTrafficProfile());
160
161         gateBuilder.setClassifiers(qosGate.getClassifiers().getClassifierContainer());
162
163         // assemble the final gate request
164         final PCMMGateReq gateReq = gateBuilder.build();
165
166         if (gateRequests.get(gatePathStr) == null) {
167             // and remember it
168             gateRequests.put(gatePathStr, gateReq);
169             // and send it to the CCAP
170             ccapClient.sendGateSet(gateReq);
171             // and wait for the COPS response to complete processing gate request
172             try {
173                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
174                 // TODO - handle this synchronization.
175                 // TODO - if not changing this, may want to make this timeout configurable
176                 synchronized (gateReq) {
177                     logger.info("Waiting 5000ms for gate request to be updated");
178                     gateReq.wait(5000);
179                     logger.debug("Gate request error - " + gateReq.getError());
180                     logger.debug("Gate request ID - " + gateReq.getGateID());
181                 }
182             } catch (Exception e) {
183                 logger.error(
184                         "PCMMService: sendGateSet(): gate response timeout exceeded for " + gatePathStr + '/' + gateReq,
185                         e);
186                 status.setDidSucceed(false);
187                 status.setMessage(String.format("408 Request Timeout - gate response timeout exceeded for %s/%s", ccap.getCcapId(),
188                         gatePathStr));
189                 return status;
190             }
191
192
193             if (gateReq.getError() != null) {
194                 gateRequests.remove(gatePathStr);
195                 status.setDidSucceed(false);
196                 status.setMessage(
197                         String.format("404 Not Found - sendGateSet for %s/%s returned error - %s", ccap.getCcapId(),
198                                 gatePathStr, gateReq.getError().toString()));
199
200                 logger.error("PCMMService: sendGateSet(): returned error: {}", gateReq.getError().toString());
201             } else {
202                 if (gateReq.getGateID() != null) {
203                     status.setDidSucceed(true);
204                     status.setCopsGateId(String.format("%08x", gateReq.getGateID().getGateID()));
205                     status.setMessage(String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
206                             ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID()) );
207                     logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
208                             gateReq.getGateID().getGateID()));
209                 } else {
210                     status.setDidSucceed(false);
211                     status.setMessage(
212                             String.format("404 Not Found - sendGateSet for %s/%s no gateId returned", ccap.getCcapId(),
213                                     gatePathStr));
214
215                     logger.info("PCMMService: sendGateSet(): no gateId returned:");
216                 }
217             }
218         } else {
219             logger.info("PCMMService: sendGateSet(): no gateId returned:");
220             status.setMessage(String.format("404 Not Found - sendGateSet for %s/%s already exists", ccap.getCcapId(), gatePathStr));
221         }
222
223         return status;
224     }
225
226     public Boolean sendGateDelete(final String gatePathStr) {
227         logger.info("sendGateDelete() - " + ccap);
228         // recover the original gate request
229         final PCMMGateReq gateReq = gateRequests.remove(gatePathStr);
230         if (gateReq != null) {
231             ccapClient.sendGateDelete(gateReq);
232             // and wait for the response to complete
233             try {
234                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
235                 // TODO - handle this synchronization.
236                 synchronized (gateReq) {
237                     gateReq.wait(1000);
238                 }
239             } catch (InterruptedException e) {
240                 logger.error("PCMMService: sendGateDelete(): gate response timeout exceeded for {}/{}", gatePathStr,
241                         gateReq);
242             }
243             if (gateReq.getError() != null) {
244                 logger.warn("PCMMService: sendGateDelete(): returned error: {}", gateReq.getError().toString());
245                 return false;
246             } else {
247                 if (gateReq.getGateID() != null) {
248                     logger.info(String.format("PCMMService: sendGateDelete(): deleted GateId %08x: ",
249                             gateReq.getGateID().getGateID()));
250                 } else {
251                     logger.error("PCMMService: sendGateDelete(): deleted but no gateId returned");
252                 }
253                 return true;
254             }
255         } else {
256             logger.warn("Attempt to delete non-existent gate with path - " + gatePathStr);
257             return false;
258                 }
259         }
260
261         public Boolean getPcmmPdpSocket() {
262                 try {
263                         return ccapClient.pcmmPdp.getSocket().isClosed();
264                 } catch (Exception e) {
265                         logger.error("getPcmmPdpSocket: {} FAILED: {}", ccapClient, e.getMessage());
266                         return true;
267                 }
268         }
269
270         public Boolean getPcmmCcapClientIsConnected() {
271                 try {
272                         return ccapClient.isConnected;
273                 } catch (Exception e) {
274                         logger.error("getPcmmCcapClientIsConnected: {} FAILED: {}", ccapClient, e.getMessage());
275                         return false;
276                 }
277         }
278
279         public String getPcmmCcapClientConnectErrMsg() {
280                 try {
281                         return ccapClient.errMessage;
282                 } catch (Exception e) {
283                         logger.error("getPcmmCcapClientIsConnected: {} FAILED: {}", ccapClient, e.getMessage());
284                         return e.getMessage();
285                 }
286         }
287
288         //new gate-info method
289         public GateSendStatus sendGateInfo(final String gatePathStr) {
290
291                 logger.info("sendGateInfo() - " + ccap);
292
293                 GateSendStatus status = new GateSendStatus();
294
295             // recover the original gate request
296             final PCMMGateReq gateReq = gateRequests.get(gatePathStr);
297
298             // is the ccap socket open?
299             final Boolean socketIsClosed = getPcmmPdpSocket();
300
301             if ((gateReq != null) && (!socketIsClosed)) {
302                 gateReq.setTransactionID(new TransactionID(gateReq.getTransactionID().getTransactionIdentifier(),
303                         ITransactionID.GateCommandType.GATE_INFO));
304
305                 ccapClient.sendGateInfo(gateReq);
306                 // and wait for the response to complete
307                 try {
308                     // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
309                     // TODO - handle this synchronization.
310                     synchronized (gateReq) {
311                         logger.info("Waiting 5000ms for gate request to be updated");
312                         gateReq.wait(5000);
313                         logger.debug("Gate request error - " + gateReq.getError());
314                         logger.debug("Gate request ID - " + gateReq.getGateID());
315                     }
316                 } catch (InterruptedException e) {
317                     status.setDidSucceed(false);
318                     status.setMessage(String.format("Gate-Info Request Timeout for %s", ccap.getCcapId()));
319                     return status;
320                 }
321                 if (gateReq.getError() != null) {
322                     status.setDidSucceed(false);
323                     status.setMessage(
324                             String.format("%s reports '%s'", ccap.getCcapId(), gateReq.getError().toString()));
325                     logger.error("PCMMService: sendGateInfo(): returned error: {}", gateReq.getError().toString());
326                 } else {
327                     if (gateReq.getGateID() != null) {
328                         status.setDidSucceed(true);
329                         status.setCopsGateId(String.format("%08x", gateReq.getGateID().getGateID()));
330                         //status.setMessage(String.format("200 OK - sendGateInfo for %s/%s returned GateId %08x",
331                         //        ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID()) );
332
333                         final IGateState gateState = gateReq.getGateState();
334                         status.setCopsGateState(gateState.getGateState().toString());
335                         status.setCopsGateStateReason(gateState.getGateStateReason().toString());
336                         status.setCopsGateTimeInfo(String.format("%d", gateReq.getGateTimeInfo().getGateTimeInfo()));
337                         status.setCopsGateUsageInfo(String.format("%d", gateReq.getGateUsageInfo().getGateUsageInfo()));
338                         logger.info(String.format("PCMMService: sendGateInfo(): returned GateId %08x: ",
339                                 gateReq.getGateID().getGateID()));
340                     } else {
341                         status.setDidSucceed(false);
342                         status.setMessage(
343                                 String.format("404 Not Found - sendGateInfo for %s/%s no gateId returned", ccap.getCcapId(),
344                                         gatePathStr));
345
346                         logger.info("PCMMService: sendGateInfo(): no gateId returned:");
347                     }
348                     return status;
349                 }
350             } else {
351                 status.setDidSucceed(false);
352                 if (socketIsClosed) {
353                         status.setMessage(String.format("%s: CCAP Cops Socket is closed",ccap.getCcapId()));
354                 }
355                 else {
356                         status.setMessage( String.format("Attempt to get info of non-existent gate with path - " + gatePathStr));
357                 }
358                 return status;
359             }
360                         return status;
361
362     }
363
364     /**
365      * Used to interface with a CCAP (including CMTSs)
366      */
367     protected class CcapClient {
368         public final PCMMPdpDataProcess pcmmProcess;
369         public final PCMMPdpAgent pcmmPdp;
370
371         private final String ipv4;
372         private final Integer port;
373
374         // Needs to be initialized in connect() method else would be final
375         protected transient PCMMPdpMsgSender pcmmSender;
376
377         private transient Boolean isConnected = false;
378         private transient String errMessage = null;
379
380         /**
381          * Constructor
382          *
383          * @param ccapIp
384          *         - the IP of the CCAP to manage
385          * @param portNum
386          *         - the port number of the CCAP to manage
387          */
388         public CcapClient(final IpAddress ccapIp, final PortNumber portNum) {
389             ipv4 = ccapIp.getIpv4Address().getValue();
390             if (portNum != null) {
391                 port = portNum.getValue();
392             } else {
393                 port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT;
394             }
395             // TODO FIXME - if this object is not null, gate processing will not work correctly
396             // TODO see - PCMMPdpReqStateMan#processReport() where the report type is success and the process is null
397             //            pcmmProcess = new PCMMPdpDataProcess();
398             pcmmProcess = null;
399             pcmmPdp = new PCMMPdpAgent(ipv4, port, clientType, pcmmProcess);
400         }
401
402         /**
403          * Starts the connection to the CCAP
404          */
405         public void connect() {
406             logger.info("Attempting to connect to host: " + ipv4 + " port: " + port);
407             errMessage = null;
408             try {
409                 pcmmPdp.connect();
410
411                 // Cannot instantiate until after pcmmPdp.connect() is called as this is where the client handle is created
412                 pcmmSender = new PCMMPdpMsgSender(clientType, pcmmPdp.getClientHandle(), pcmmPdp.getSocket());
413
414                 isConnected = true;
415             } catch (Exception e) {
416                 isConnected = false;
417                 logger.error("Failed to connect to host: " + ipv4 + " port: " + port, e);
418                 errMessage = e.getMessage();
419             }
420         }
421
422         public void disconnect() {
423             logger.info("CcapClient: disconnect(): {}:{}", ipv4, port);
424             pcmmPdp.disconnect(new COPSError(ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA));
425             isConnected = false;
426         }
427
428         // TODO - consider returning a new PCMMGateReq object or a future here instead of setting the ID on the old
429         // TODO - request by reference which makes the code more convoluted thus making issues more difficult to track down.
430         public Boolean sendGateSet(final PCMMGateReq gateReq) {
431             logger.info("CcapClient: sendGateSet(): {}:{} => {}", ipv4, port, gateReq);
432             try {
433                 pcmmSender.sendGateSet(gateReq);
434
435                 // TODO - determine if this is the correct place to perform this operation as this currently is the
436                 // TODO - place where the gate ID can be set on the gateReq object
437                 //                pcmmSender.handleGateReport(pcmmPdp.getSocket());
438             } catch (COPSPdpException e) {
439                 logger.error("CcapClient: sendGateSet(): {}:{} => {} FAILED:", ipv4, port, gateReq, e);
440             }
441             // and save it back to the gateRequest object for gate delete later
442             gateReq.setGateID(pcmmSender.getGateID());
443
444             // TODO - determine why this method is always returning true???
445             return true;
446         }
447
448         public Boolean sendGateDelete(final PCMMGateReq gateReq) {
449             logger.info("CcapClient: sendGateDelete(): {}:{} => {}", ipv4, port, gateReq);
450             try {
451                 pcmmSender.sendGateDelete(gateReq);
452             } catch (COPSPdpException e) {
453                 logger.error("CcapClient: sendGateDelete(): {}:{} => {} FAILED: {}", ipv4, port, gateReq,
454                         e.getMessage());
455             }
456             return true;
457         }
458
459         public Boolean sendGateInfo(final PCMMGateReq gateReq) {
460             logger.info("CcapClient: sendGateInfo(): {}:{} => {}", ipv4, port);
461             try {
462                 pcmmSender.sendGateInfo(gateReq);
463             } catch (COPSPdpException e) {
464                 logger.error("CcapClient: sendGateInfo(): {}:{} => {} FAILED: {}", ipv4, port,
465                         e.getMessage());
466             }
467             return true;
468         }
469     }
470 }
471