Adding support for multiple classifiers per gate
[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.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         gateBuilder.setClassifiers(qosGate.getClassifiers().getClassifierContainer());
127
128         // assemble the final gate request
129         final PCMMGateReq gateReq = gateBuilder.build();
130
131         if (gateRequests.get(gatePathStr) == null) {
132             // and remember it
133             gateRequests.put(gatePathStr, gateReq);
134             // and send it to the CCAP
135             ccapClient.sendGateSet(gateReq);
136             // and wait for the COPS response to complete processing gate request
137             try {
138                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
139                 // TODO - handle this synchronization.
140                 // TODO - if not changing this, may want to make this timeout configurable
141                 synchronized (gateReq) {
142                     logger.info("Waiting 5000ms for gate request to be updated");
143                     gateReq.wait(5000);
144                     logger.debug("Gate request error - " + gateReq.getError());
145                     logger.debug("Gate request ID - " + gateReq.getGateID());
146                 }
147             } catch (Exception e) {
148                 logger.error(
149                         "PCMMService: sendGateSet(): gate response timeout exceeded for " + gatePathStr + '/' + gateReq,
150                         e);
151                 status.setDidSucceed(false);
152                 status.setMessage(String.format("408 Request Timeout - gate response timeout exceeded for %s/%s", ccap.getCcapId(),
153                         gatePathStr));
154                 return status;
155             }
156
157
158             if (gateReq.getError() != null) {
159                 gateRequests.remove(gatePathStr);
160                 status.setDidSucceed(false);
161                 status.setMessage(
162                         String.format("404 Not Found - sendGateSet for %s/%s returned error - %s", ccap.getCcapId(),
163                                 gatePathStr, gateReq.getError().toString()));
164
165                 logger.error("PCMMService: sendGateSet(): returned error: {}", gateReq.getError().toString());
166             } else {
167                 if (gateReq.getGateID() != null) {
168                     status.setDidSucceed(true);
169                     status.setCopsGateId(String.format("%08x", gateReq.getGateID().getGateID()));
170                     status.setMessage(String.format("200 OK - sendGateSet for %s/%s returned GateId %08x",
171                             ccap.getCcapId(), gatePathStr, gateReq.getGateID().getGateID()) );
172                     logger.info(String.format("PCMMService: sendGateSet(): returned GateId %08x: ",
173                             gateReq.getGateID().getGateID()));
174                 } else {
175                     status.setDidSucceed(false);
176                     status.setMessage(
177                             String.format("404 Not Found - sendGateSet for %s/%s no gateId returned", ccap.getCcapId(),
178                                     gatePathStr));
179
180                     logger.info("PCMMService: sendGateSet(): no gateId returned:");
181                 }
182             }
183         } else {
184             logger.info("PCMMService: sendGateSet(): no gateId returned:");
185             status.setMessage(String.format("404 Not Found - sendGateSet for %s/%s already exists", ccap.getCcapId(), gatePathStr));
186         }
187
188         return status;
189     }
190
191     public Boolean sendGateDelete(final String gatePathStr) {
192         logger.info("sendGateDelete() - " + ccap);
193         // recover the original gate request
194         final PCMMGateReq gateReq = gateRequests.remove(gatePathStr);
195         if (gateReq != null) {
196             ccapClient.sendGateDelete(gateReq);
197             // and wait for the response to complete
198             try {
199                 // TODO - see PCMMPdpReqStateMan#processReport() gate.notify(). Should determine a better means to
200                 // TODO - handle this synchronization.
201                 synchronized (gateReq) {
202                     gateReq.wait(1000);
203                 }
204             } catch (InterruptedException e) {
205                 logger.error("PCMMService: sendGateDelete(): gate response timeout exceeded for {}/{}", gatePathStr,
206                         gateReq);
207             }
208             if (gateReq.getError() != null) {
209                 logger.warn("PCMMService: sendGateDelete(): returned error: {}", gateReq.getError().toString());
210                 return false;
211             } else {
212                 if (gateReq.getGateID() != null) {
213                     logger.info(String.format("PCMMService: sendGateDelete(): deleted GateId %08x: ",
214                             gateReq.getGateID().getGateID()));
215                 } else {
216                     logger.error("PCMMService: sendGateDelete(): deleted but no gateId returned");
217                 }
218                 return true;
219             }
220         } else {
221             logger.warn("Attempt to delete non-existent gate with path - " + gatePathStr);
222             return false;
223         }
224     }
225
226     /**
227      * Used to interface with a CCAP (including CMTSs)
228      */
229     protected class CcapClient {
230         public final PCMMPdpDataProcess pcmmProcess;
231         public final PCMMPdpAgent pcmmPdp;
232
233         private final String ipv4;
234         private final Integer port;
235
236         // Needs to be initialized in connect() method else would be final
237         protected transient PCMMPdpMsgSender pcmmSender;
238
239         private transient Boolean isConnected = false;
240         private transient String errMessage = null;
241
242         /**
243          * Constructor
244          *
245          * @param ccapIp
246          *         - the IP of the CCAP to manage
247          * @param portNum
248          *         - the port number of the CCAP to manage
249          */
250         public CcapClient(final IpAddress ccapIp, final PortNumber portNum) {
251             ipv4 = ccapIp.getIpv4Address().getValue();
252             if (portNum != null) {
253                 port = portNum.getValue();
254             } else {
255                 port = PCMMPdpAgent.WELL_KNOWN_PDP_PORT;
256             }
257             // TODO FIXME - if this object is not null, gate processing will not work correctly
258             // TODO see - PCMMPdpReqStateMan#processReport() where the report type is success and the process is null
259             //            pcmmProcess = new PCMMPdpDataProcess();
260             pcmmProcess = null;
261             pcmmPdp = new PCMMPdpAgent(ipv4, port, clientType, pcmmProcess);
262         }
263
264         /**
265          * Starts the connection to the CCAP
266          */
267         public void connect() {
268             logger.info("Attempting to connect to host: " + ipv4 + " port: " + port);
269             try {
270                 pcmmPdp.connect();
271
272                 // Cannot instantiate until after pcmmPdp.connect() is called as this is where the client handle is created
273                 pcmmSender = new PCMMPdpMsgSender(clientType, pcmmPdp.getClientHandle(), pcmmPdp.getSocket());
274
275                 isConnected = true;
276             } catch (Exception e) {
277                 isConnected = false;
278                 logger.error("Failed to connect to host: " + ipv4 + " port: " + port, e);
279                 errMessage = e.getMessage();
280             }
281         }
282
283         public void disconnect() {
284             logger.info("CcapClient: disconnect(): {}:{}", ipv4, port);
285             pcmmPdp.disconnect(new COPSError(ErrorTypes.SHUTTING_DOWN, ErrorTypes.NA));
286             isConnected = false;
287         }
288
289         // TODO - consider returning a new PCMMGateReq object or a future here instead of setting the ID on the old
290         // TODO - request by reference which makes the code more convoluted thus making issues more difficult to track down.
291         public Boolean sendGateSet(final PCMMGateReq gateReq) {
292             logger.info("CcapClient: sendGateSet(): {}:{} => {}", ipv4, port, gateReq);
293             try {
294                 pcmmSender.sendGateSet(gateReq);
295
296                 // TODO - determine if this is the correct place to perform this operation as this currently is the
297                 // TODO - place where the gate ID can be set on the gateReq object
298                 //                pcmmSender.handleGateReport(pcmmPdp.getSocket());
299             } catch (COPSPdpException e) {
300                 logger.error("CcapClient: sendGateSet(): {}:{} => {} FAILED:", ipv4, port, gateReq, e);
301             }
302             // and save it back to the gateRequest object for gate delete later
303             gateReq.setGateID(pcmmSender.getGateID());
304
305             // TODO - determine why this method is always returning true???
306             return true;
307         }
308
309         public Boolean sendGateDelete(final PCMMGateReq gateReq) {
310             logger.info("CcapClient: sendGateDelete(): {}:{} => {}", ipv4, port, gateReq);
311             try {
312                 pcmmSender.sendGateDelete(gateReq);
313             } catch (COPSPdpException e) {
314                 logger.error("CcapClient: sendGateDelete(): {}:{} => {} FAILED: {}", ipv4, port, gateReq,
315                         e.getMessage());
316             }
317             return true;
318         }
319     }
320 }
321