Added response data to operational datastore, refactored data validation
[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.rev151026.ServiceClassName;
17 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ServiceFlowDirection;
18 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.ccaps.Ccap;
19 import org.opendaylight.yang.gen.v1.urn.packetcable.rev151026.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.impl.PCMMGateReq;
24 import org.slf4j.Logger;
25 import org.slf4j.LoggerFactory;
26 import org.umu.cops.prpdp.COPSPdpException;
27 import org.umu.cops.stack.COPSError;
28 import org.umu.cops.stack.COPSError.ErrorTypes;
29
30 /**
31  * Class responsible for managing the gates for a single CCAP.
32  */
33 @ThreadSafe
34 public class PCMMService {
35     private final Logger logger = LoggerFactory.getLogger(PCMMService.class);
36
37     private final Ccap ccap;
38     private final IpAddress ipAddr;
39     private final PortNumber portNum;
40     protected final CcapClient ccapClient;
41     protected Map<String, PCMMGateReq> gateRequests = Maps.newConcurrentMap();
42
43     private final short clientType;
44
45     public PCMMService(final short clientType, final Ccap ccap) {
46         this.clientType = clientType;
47         this.ccap = ccap;
48         ipAddr = ccap.getConnection().getIpAddress();
49         portNum = ccap.getConnection().getPort();
50         ccapClient = new CcapClient(ipAddr, portNum);
51         logger.info("Attempting to add CCAP with ID {} @ {}:{}", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(),
52                 portNum.getValue());
53     }
54
55     public void disconect() {
56         ccapClient.disconnect();
57     }
58
59     // TODO - try and change the return to something other than a String to be parsed to determine success
60     public String addCcap() {
61         ccapClient.connect();
62         if (ccapClient.isConnected) {
63             logger.info("Connected to CCAP with ID - " + ccap.getCcapId());
64             return String
65                     .format("200 OK - CCAP %s connected @ %s:%d", ccap.getCcapId(), ipAddr.getIpv4Address().getValue(),
66                             portNum.getValue());
67         } else {
68             return String.format("404 Not Found - CCAP %s failed to connect @ %s:%d - %s", ccap.getCcapId(),
69                     ipAddr.getIpv4Address().getValue(), portNum.getValue(), ccapClient.errMessage);
70         }
71     }
72
73     public class GateSetStatus {
74         private boolean didSucceed = false;
75         private String message = "";
76         private String copsGateId = "";
77
78         public boolean didSucceed() {
79             return didSucceed;
80         }
81
82         void setDidSucceed(final boolean didSucceed) {
83             this.didSucceed = didSucceed;
84         }
85
86         public String getMessage() {
87             return message;
88         }
89
90         void setMessage(final String message) {
91             this.message = message;
92         }
93
94         public String getCopsGateId() {
95             return copsGateId;
96         }
97
98         void setCopsGateId(final String copsGateId) {
99             this.copsGateId = copsGateId;
100         }
101
102     }
103
104     public GateSetStatus sendGateSet(final String gatePathStr, final InetAddress subId, final Gate qosGate,
105             final ServiceFlowDirection scnDir) {
106
107         GateSetStatus status = new GateSetStatus();
108
109         logger.info("Sending gate to CCAP with ID - " + ccap.getCcapId());
110
111         // assemble the gate request for this subId
112         final PCMMGateReqBuilder gateBuilder = new PCMMGateReqBuilder();
113         gateBuilder.setAmId(ccap.getAmId());
114         gateBuilder.setSubscriberId(subId);
115
116         // force gateSpec.Direction to align with SCN direction
117         final ServiceClassName scn = qosGate.getTrafficProfile().getServiceClassName();
118         if (scn != null) {
119             gateBuilder.setGateSpec(qosGate.getGateSpec(), scnDir);
120         } else {
121             // not an SCN gate
122             gateBuilder.setGateSpec(qosGate.getGateSpec(), null);
123         }
124         gateBuilder.setTrafficProfile(qosGate.getTrafficProfile());
125
126         // pick a classifier type (only one for now)
127         if (qosGate.getClassifier() != null) {
128             gateBuilder.setClassifier(qosGate.getClassifier());
129         } else if (qosGate.getExtClassifier() != null) {
130             gateBuilder.setExtClassifier(qosGate.getExtClassifier());
131         } else if (qosGate.getIpv6Classifier() != null) {
132             gateBuilder.setIpv6Classifier(qosGate.getIpv6Classifier());
133         }
134
135         // assemble the final gate request
136         final PCMMGateReq gateReq = gateBuilder.build();
137
138         if (gateRequests.get(gatePathStr) == null) {
139             // and remember it
140             gateRequests.put(gatePathStr, gateReq);
141             // and send it to the CCAP
142             ccapClient.sendGateSet(gateReq);
143             // and wait for the COPS response to complete processing gate request
144             try {
145                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
146                 // TODO - handle this synchronization.
147                 // TODO - if not changing this, may want to make this timeout configurable
148                 synchronized (gateReq) {
149                     logger.info("Waiting 5000ms for gate request to be updated");
150                     gateReq.wait(5000);
151                     logger.debug("Gate request error - " + gateReq.getError());
152                     logger.debug("Gate request ID - " + gateReq.getGateID());
153                 }
154             } catch (Exception e) {
155                 logger.error(
156                         "PCMMService: sendGateSet(): gate response timeout exceeded for " + gatePathStr + '/' + gateReq,
157                         e);
158                 status.setDidSucceed(false);
159                 status.setMessage(String.format("408 Request Timeout - gate response timeout exceeded for %s/%s", ccap.getCcapId(),
160                         gatePathStr));
161                 return status;
162             }
163
164
165             if (gateReq.getError() != null) {
166                 status.setDidSucceed(false);
167                 status.setMessage(
168                         String.format("404 Not Found - sendGateSet for %s/%s returned error - %s", ccap.getCcapId(),
169                                 gatePathStr, gateReq.getError().toString()));
170
171                 logger.error("PCMMService: sendGateSet(): returned error: {}", gateReq.getError().toString());
172             } else {
173                 if (gateReq.getGateID() != null) {
174                     status.setDidSucceed(true);
175                     status.setCopsGateId(String.format("%08x", gateReq.getGateID().getGateID()));
176                     status.setMessage(String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
177                             ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID()) );
178                     logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
179                             gateReq.getGateID().getGateID()));
180                 } else {
181                     status.setDidSucceed(false);
182                     status.setMessage(
183                             String.format("404 Not Found - sendGateSet for %s/%s no gateId returned", ccap.getCcapId(),
184                                     gatePathStr));
185
186                     logger.info("PCMMService: sendGateSet(): no gateId returned:");
187                 }
188             }
189         } else {
190             logger.info("PCMMService: sendGateSet(): no gateId returned:");
191             status.setMessage(String.format("404 Not Found - sendGateSet for %s/%s already exists", ccap.getCcapId(), gatePathStr));
192         }
193
194         return status;
195     }
196
197     public Boolean sendGateDelete(final String gatePathStr) {
198         logger.info("sendGateDelete() - " + ccap);
199         // recover the original gate request
200         final PCMMGateReq gateReq = gateRequests.remove(gatePathStr);
201         if (gateReq != null) {
202             ccapClient.sendGateDelete(gateReq);
203             // and wait for the response to complete
204             try {
205                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
206                 // TODO - handle this synchronization.
207                 synchronized (gateReq) {
208                     gateReq.wait(1000);
209                 }
210             } catch (InterruptedException e) {
211                 logger.error("PCMMService: sendGateDelete(): gate response timeout exceeded for {}/{}", gatePathStr,
212                         gateReq);
213             }
214             if (gateReq.getError() != null) {
215                 logger.warn("PCMMService: sendGateDelete(): returned error: {}", gateReq.getError().toString());
216                 return false;
217             } else {
218                 if (gateReq.getGateID() != null) {
219                     logger.info(String.format("PCMMService: sendGateDelete(): deleted GateId %08x: ",
220                             gateReq.getGateID().getGateID()));
221                 } else {
222                     logger.error("PCMMService: sendGateDelete(): deleted but no gateId returned");
223                 }
224                 return true;
225             }
226         } else {
227             logger.warn("Attempt to delete non-existent gate with path - " + gatePathStr);
228             return false;
229         }
230     }
231
232     /**
233      * Used to interface with a CCAP (including CMTSs)
234      */
235     protected class CcapClient {
236         public final PCMMPdpDataProcess pcmmProcess;
237         public final PCMMPdpAgent pcmmPdp;
238
239         private final String ipv4;
240         private final Integer port;
241
242         // Needs to be initialized in connect() method else would be final
243         protected transient PCMMPdpMsgSender pcmmSender;
244
245         private transient Boolean isConnected = false;
246         private transient String errMessage = null;
247
248         /**
249          * Constructor
250          *
251          * @param ccapIp
252          *         - the IP of the CCAP to manage
253          * @param portNum
254          *         - the port number of the CCAP to manage
255          */
256         public CcapClient(final IpAddress ccapIp, final PortNumber portNum) {
257             ipv4 = ccapIp.getIpv4Address().getValue();
258             if (portNum != null) {
259                 port = portNum.getValue();
260             } else {
261                 port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT;
262             }
263             // TODO FIXME - if this object is not null, gate processing will not work correctly
264             // TODO see - PCMMPdpReqStateMan#processReport() where the report type is success and the process is null
265             //            pcmmProcess = new PCMMPdpDataProcess();
266             pcmmProcess = null;
267             pcmmPdp = new PCMMPdpAgent(ipv4, port, clientType, pcmmProcess);
268         }
269
270         /**
271          * Starts the connection to the CCAP
272          */
273         public void connect() {
274             logger.info("Attempting to connect to host: " + ipv4 + " port: " + port);
275             try {
276                 pcmmPdp.connect();
277
278                 // Cannot instantiate until after pcmmPdp.connect() is called as this is where the client handle is created
279                 pcmmSender = new PCMMPdpMsgSender(clientType, pcmmPdp.getClientHandle(), pcmmPdp.getSocket());
280
281                 isConnected = true;
282             } catch (Exception e) {
283                 isConnected = false;
284                 logger.error("Failed to connect to host: " + ipv4 + " port: " + port, e);
285                 errMessage = e.getMessage();
286             }
287         }
288
289         public void disconnect() {
290             logger.info("CcapClient: disconnect(): {}:{}", ipv4, port);
291             pcmmPdp.disconnect(new COPSError(ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA));
292             isConnected = false;
293         }
294
295         // TODO - consider returning a new PCMMGateReq object or a future here instead of setting the ID on the old
296         // TODO - request by reference which makes the code more convoluted thus making issues more difficult to track down.
297         public Boolean sendGateSet(final PCMMGateReq gateReq) {
298             logger.info("CcapClient: sendGateSet(): {}:{} => {}", ipv4, port, gateReq);
299             try {
300                 pcmmSender.sendGateSet(gateReq);
301
302                 // TODO - determine if this is the correct place to perform this operation as this currently is the
303                 // TODO - place where the gate ID can be set on the gateReq object
304                 //                pcmmSender.handleGateReport(pcmmPdp.getSocket());
305             } catch (COPSPdpException e) {
306                 logger.error("CcapClient: sendGateSet(): {}:{} => {} FAILED:", ipv4, port, gateReq, e);
307             }
308             // and save it back to the gateRequest object for gate delete later
309             gateReq.setGateID(pcmmSender.getGateID());
310
311             // TODO - determine why this method is always returning true???
312             return true;
313         }
314
315         public Boolean sendGateDelete(final PCMMGateReq gateReq) {
316             logger.info("CcapClient: sendGateDelete(): {}:{} => {}", ipv4, port, gateReq);
317             try {
318                 pcmmSender.sendGateDelete(gateReq);
319             } catch (COPSPdpException e) {
320                 logger.error("CcapClient: sendGateDelete(): {}:{} => {} FAILED: {}", ipv4, port, gateReq,
321                         e.getMessage());
322             }
323             return true;
324         }
325     }
326 }
327