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