Updates to support new TrafficProfiles that require a user-specified Direction in...
[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.rev130715.IpAddress;
15 import org.opendaylight.yang.gen.v1.urn.ietf.params.xml.ns.yang.ietf.inet.types.rev130715.PortNumber;
16 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ServiceClassName;
17 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ServiceFlowDirection;
18 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.ccaps.Ccap;
19 import org.opendaylight.yang.gen.v1.urn.packetcable.rev161017.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
141         GateSendStatus status = new GateSendStatus();
142
143         logger.info("Sending gate to CCAP with ID - " + ccap.getCcapId());
144
145         // assemble the gate request for this subId
146         final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
147         gateBuilder.setAmId(ccap.getAmId());
148         gateBuilder.setSubscriberId(subId);
149         gateBuilder.setGateSpec(qosGate.getGateSpec(), null);
150         gateBuilder.setTrafficProfile(qosGate.getTrafficProfile());
151         gateBuilder.setClassifiers(qosGate.getClassifiers().getClassifierContainer());
152
153         // assemble the final gate request
154         final PCMMGateReq gateReq = gateBuilder.build();
155
156         if (gateRequests.get(gatePathStr) == null) {
157             // and remember it
158             gateRequests.put(gatePathStr, gateReq);
159             // and send it to the CCAP
160             ccapClient.sendGateSet(gateReq);
161             // and wait for the COPS response to complete processing gate request
162             try {
163                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
164                 // TODO - handle this synchronization.
165                 // TODO - if not changing this, may want to make this timeout configurable
166                 synchronized (gateReq) {
167                     logger.info("Waiting 5000ms for gate request to be updated");
168                     gateReq.wait(5000);
169                     logger.debug("Gate request error - " + gateReq.getError());
170                     logger.debug("Gate request ID - " + gateReq.getGateID());
171                 }
172             } catch (Exception e) {
173                 logger.error(
174                         "PCMMService: sendGateSet(): gate response timeout exceeded for " + gatePathStr + '/' + gateReq,
175                         e);
176                 status.setDidSucceed(false);
177                 status.setMessage(String.format("408 Request Timeout - gate response timeout exceeded for %s/%s", ccap.getCcapId(),
178                         gatePathStr));
179                 return status;
180             }
181
182
183             if (gateReq.getError() != null) {
184                 gateRequests.remove(gatePathStr);
185                 status.setDidSucceed(false);
186                 status.setMessage(
187                         String.format("404 Not Found - sendGateSet for %s/%s returned error - %s", ccap.getCcapId(),
188                                 gatePathStr, gateReq.getError().toString()));
189
190                 logger.error("PCMMService: sendGateSet(): returned error: {}", gateReq.getError().toString());
191             } else {
192                 if (gateReq.getGateID() != null) {
193                     status.setDidSucceed(true);
194                     status.setCopsGateId(String.format("%08x", gateReq.getGateID().getGateID()));
195                     status.setMessage(String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
196                             ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID()) );
197                     logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
198                             gateReq.getGateID().getGateID()));
199                 } else {
200                     status.setDidSucceed(false);
201                     status.setMessage(
202                             String.format("404 Not Found - sendGateSet for %s/%s no gateId returned", ccap.getCcapId(),
203                                     gatePathStr));
204
205                     logger.info("PCMMService: sendGateSet(): no gateId returned:");
206                 }
207             }
208         } else {
209             logger.info("PCMMService: sendGateSet(): no gateId returned:");
210             status.setMessage(String.format("404 Not Found - sendGateSet for %s/%s already exists", ccap.getCcapId(), gatePathStr));
211         }
212
213         return status;
214     }
215
216     public Boolean sendGateDelete(final String gatePathStr) {
217         logger.info("sendGateDelete() - " + ccap);
218         // recover the original gate request
219         final PCMMGateReq gateReq = gateRequests.remove(gatePathStr);
220         if (gateReq != null) {
221             ccapClient.sendGateDelete(gateReq);
222             // and wait for the response to complete
223             try {
224                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
225                 // TODO - handle this synchronization.
226                 synchronized (gateReq) {
227                     gateReq.wait(1000);
228                 }
229             } catch (InterruptedException e) {
230                 logger.error("PCMMService: sendGateDelete(): gate response timeout exceeded for {}/{}", gatePathStr,
231                         gateReq);
232             }
233             if (gateReq.getError() != null) {
234                 logger.warn("PCMMService: sendGateDelete(): returned error: {}", gateReq.getError().toString());
235                 return false;
236             } else {
237                 if (gateReq.getGateID() != null) {
238                     logger.info(String.format("PCMMService: sendGateDelete(): deleted GateId %08x: ",
239                             gateReq.getGateID().getGateID()));
240                 } else {
241                     logger.error("PCMMService: sendGateDelete(): deleted but no gateId returned");
242                 }
243                 return true;
244             }
245         } else {
246             logger.warn("Attempt to delete non-existent gate with path - " + gatePathStr);
247             return false;
248                 }
249         }
250
251         public Boolean getPcmmPdpSocket() {
252                 try {
253                         return ccapClient.pcmmPdp.getSocket().isClosed();
254                 } catch (Exception e) {
255                         logger.error("getPcmmPdpSocket: {} FAILED: {}", ccapClient, e.getMessage());
256                         return true;
257                 }
258         }
259
260         public Boolean getPcmmCcapClientIsConnected() {
261                 try {
262                         return ccapClient.isConnected;
263                 } catch (Exception e) {
264                         logger.error("getPcmmCcapClientIsConnected: {} FAILED: {}", ccapClient, e.getMessage());
265                         return false;
266                 }
267         }
268
269         public String getPcmmCcapClientConnectErrMsg() {
270                 try {
271                         return ccapClient.errMessage;
272                 } catch (Exception e) {
273                         logger.error("getPcmmCcapClientIsConnected: {} FAILED: {}", ccapClient, e.getMessage());
274                         return e.getMessage();
275                 }
276         }
277
278         //new gate-info method
279         public GateSendStatus sendGateInfo(final String gatePathStr) {
280
281                 logger.info("sendGateInfo() - " + ccap);
282
283                 GateSendStatus status = new GateSendStatus();
284
285             // recover the original gate request
286             final PCMMGateReq gateReq = gateRequests.get(gatePathStr);
287
288             // is the ccap socket open?
289             final Boolean socketIsClosed = getPcmmPdpSocket();
290
291             if ((gateReq != null) && (!socketIsClosed)) {
292                 gateReq.setTransactionID(new TransactionID(gateReq.getTransactionID().getTransactionIdentifier(),
293                         ITransactionID.GateCommandType.GATE_INFO));
294
295                 ccapClient.sendGateInfo(gateReq);
296                 // and wait for the response to complete
297                 try {
298                     // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
299                     // TODO - handle this synchronization.
300                     synchronized (gateReq) {
301                         logger.info("Waiting 5000ms for gate request to be updated");
302                         gateReq.wait(5000);
303                         logger.debug("Gate request error - " + gateReq.getError());
304                         logger.debug("Gate request ID - " + gateReq.getGateID());
305                     }
306                 } catch (InterruptedException e) {
307                     status.setDidSucceed(false);
308                     status.setMessage(String.format("Gate-Info Request Timeout for %s", ccap.getCcapId()));
309                     return status;
310                 }
311                 if (gateReq.getError() != null) {
312                     status.setDidSucceed(false);
313                     status.setMessage(
314                             String.format("%s reports '%s'", ccap.getCcapId(), gateReq.getError().toString()));
315                     logger.error("PCMMService: sendGateInfo(): returned error: {}", gateReq.getError().toString());
316                 } else {
317                     if (gateReq.getGateID() != null) {
318                         status.setDidSucceed(true);
319                         status.setCopsGateId(String.format("%08x", gateReq.getGateID().getGateID()));
320                         //status.setMessage(String.format("200 OK - sendGateInfo for %s/%s returned GateId %08x",
321                         //        ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID()) );
322
323                         final IGateState gateState = gateReq.getGateState();
324                         status.setCopsGateState(gateState.getGateState().toString());
325                         status.setCopsGateStateReason(gateState.getGateStateReason().toString());
326                         status.setCopsGateTimeInfo(String.format("%d", gateReq.getGateTimeInfo().getGateTimeInfo()));
327                         status.setCopsGateUsageInfo(String.format("%d", gateReq.getGateUsageInfo().getGateUsageInfo()));
328                         logger.info(String.format("PCMMService: sendGateInfo(): returned GateId %08x: ",
329                                 gateReq.getGateID().getGateID()));
330                     } else {
331                         status.setDidSucceed(false);
332                         status.setMessage(
333                                 String.format("404 Not Found - sendGateInfo for %s/%s no gateId returned", ccap.getCcapId(),
334                                         gatePathStr));
335
336                         logger.info("PCMMService: sendGateInfo(): no gateId returned:");
337                     }
338                     return status;
339                 }
340             } else {
341                 status.setDidSucceed(false);
342                 if (socketIsClosed) {
343                         status.setMessage(String.format("%s: CCAP Cops Socket is closed",ccap.getCcapId()));
344                 }
345                 else {
346                         status.setMessage( String.format("Attempt to get info of non-existent gate with path - " + gatePathStr));
347                 }
348                 return status;
349             }
350                         return status;
351
352     }
353
354     /**
355      * Used to interface with a CCAP (including CMTSs)
356      */
357     protected class CcapClient {
358         public final PCMMPdpDataProcess pcmmProcess;
359         public final PCMMPdpAgent pcmmPdp;
360
361         private final String ipv4;
362         private final Integer port;
363
364         // Needs to be initialized in connect() method else would be final
365         protected transient PCMMPdpMsgSender pcmmSender;
366
367         private transient Boolean isConnected = false;
368         private transient String errMessage = null;
369
370         /**
371          * Constructor
372          *
373          * @param ccapIp
374          *         - the IP of the CCAP to manage
375          * @param portNum
376          *         - the port number of the CCAP to manage
377          */
378         public CcapClient(final IpAddress ccapIp, final PortNumber portNum) {
379             ipv4 = ccapIp.getIpv4Address().getValue();
380             if (portNum != null) {
381                 port = portNum.getValue();
382             } else {
383                 port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT;
384             }
385             // TODO FIXME - if this object is not null, gate processing will not work correctly
386             // TODO see - PCMMPdpReqStateMan#processReport() where the report type is success and the process is null
387             //            pcmmProcess = new PCMMPdpDataProcess();
388             pcmmProcess = null;
389             pcmmPdp = new PCMMPdpAgent(ipv4, port, clientType, pcmmProcess);
390         }
391
392         /**
393          * Starts the connection to the CCAP
394          */
395         public void connect() {
396             logger.info("Attempting to connect to host: " + ipv4 + " port: " + port);
397             errMessage = null;
398             try {
399                 pcmmPdp.connect();
400
401                 // Cannot instantiate until after pcmmPdp.connect() is called as this is where the client handle is created
402                 pcmmSender = new PCMMPdpMsgSender(clientType, pcmmPdp.getClientHandle(), pcmmPdp.getSocket());
403
404                 isConnected = true;
405             } catch (Exception e) {
406                 isConnected = false;
407                 logger.error("Failed to connect to host: " + ipv4 + " port: " + port, e);
408                 errMessage = e.getMessage();
409             }
410         }
411
412         public void disconnect() {
413             logger.info("CcapClient: disconnect(): {}:{}", ipv4, port);
414             pcmmPdp.disconnect(new COPSError(ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA));
415             isConnected = false;
416         }
417
418         // TODO - consider returning a new PCMMGateReq object or a future here instead of setting the ID on the old
419         // TODO - request by reference which makes the code more convoluted thus making issues more difficult to track down.
420         public Boolean sendGateSet(final PCMMGateReq gateReq) {
421             logger.info("CcapClient: sendGateSet(): {}:{} => {}", ipv4, port, gateReq);
422             try {
423                 pcmmSender.sendGateSet(gateReq);
424
425                 // TODO - determine if this is the correct place to perform this operation as this currently is the
426                 // TODO - place where the gate ID can be set on the gateReq object
427                 //                pcmmSender.handleGateReport(pcmmPdp.getSocket());
428             } catch (COPSPdpException e) {
429                 logger.error("CcapClient: sendGateSet(): {}:{} => {} FAILED:", ipv4, port, gateReq, e);
430             }
431             // and save it back to the gateRequest object for gate delete later
432             gateReq.setGateID(pcmmSender.getGateID());
433
434             // TODO - determine why this method is always returning true???
435             return true;
436         }
437
438         public Boolean sendGateDelete(final PCMMGateReq gateReq) {
439             logger.info("CcapClient: sendGateDelete(): {}:{} => {}", ipv4, port, gateReq);
440             try {
441                 pcmmSender.sendGateDelete(gateReq);
442             } catch (COPSPdpException e) {
443                 logger.error("CcapClient: sendGateDelete(): {}:{} => {} FAILED: {}", ipv4, port, gateReq,
444                         e.getMessage());
445             }
446             return true;
447         }
448
449         public Boolean sendGateInfo(final PCMMGateReq gateReq) {
450             logger.info("CcapClient: sendGateInfo(): {}:{} => {}", ipv4, port);
451             try {
452                 pcmmSender.sendGateInfo(gateReq);
453             } catch (COPSPdpException e) {
454                 logger.error("CcapClient: sendGateInfo(): {}:{} => {} FAILED: {}", ipv4, port,
455                         e.getMessage());
456             }
457             return true;
458         }
459     }
460 }
461