Promote MessageRegistry to pcep-api
[bgpcep.git] / pcep / ietf-stateful / src / main / java / org / opendaylight / protocol / pcep / ietf / stateful / StatefulPCReportMessageParser.java
1 /*
2  * Copyright (c) 2013 Cisco Systems, Inc. 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.protocol.pcep.ietf.stateful;
9
10 import static com.google.common.base.Preconditions.checkArgument;
11
12 import io.netty.buffer.ByteBuf;
13 import io.netty.buffer.Unpooled;
14 import java.util.ArrayList;
15 import java.util.List;
16 import java.util.Optional;
17 import java.util.Queue;
18 import org.opendaylight.protocol.pcep.PCEPDeserializerException;
19 import org.opendaylight.protocol.pcep.spi.AbstractMessageParser;
20 import org.opendaylight.protocol.pcep.spi.MessageUtil;
21 import org.opendaylight.protocol.pcep.spi.ObjectRegistry;
22 import org.opendaylight.protocol.pcep.spi.PCEPErrors;
23 import org.opendaylight.protocol.pcep.spi.PSTUtil;
24 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.Pcrpt;
25 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.PcrptBuilder;
26 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp.object.Lsp;
27 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcrpt.message.PcrptMessageBuilder;
28 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcrpt.message.pcrpt.message.Reports;
29 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcrpt.message.pcrpt.message.ReportsBuilder;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcrpt.message.pcrpt.message.reports.Path;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.pcrpt.message.pcrpt.message.reports.PathBuilder;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.srp.object.Srp;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.srp.object.srp.Tlvs;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.Message;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.Object;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.bandwidth.object.Bandwidth;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.explicit.route.object.Ero;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.include.route.object.Iro;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.lsp.attributes.Metrics;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.lsp.attributes.MetricsBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.lspa.object.Lspa;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.metric.object.Metric;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109.reported.route.object.Rro;
44
45 /**
46  * Parser for {@link Pcrpt}.
47  */
48 public class StatefulPCReportMessageParser extends AbstractMessageParser {
49
50     public static final int TYPE = 10;
51
52     public StatefulPCReportMessageParser(final ObjectRegistry registry) {
53         super(registry);
54     }
55
56     @Override
57     public void serializeMessage(final Message message, final ByteBuf out) {
58         checkArgument(message instanceof Pcrpt, "Wrong instance of Message. Passed instance of %s. Need Pcrpt.",
59             message.getClass());
60         final Pcrpt msg = (Pcrpt) message;
61         final List<Reports> reports = msg.getPcrptMessage().getReports();
62         final ByteBuf buffer = Unpooled.buffer();
63         for (final Reports report : reports) {
64             serializeReport(report, buffer);
65         }
66         MessageUtil.formatMessage(TYPE, buffer, out);
67     }
68
69     private void serializeReport(final Reports report, final ByteBuf buffer) {
70         if (report.getSrp() != null) {
71             serializeObject(report.getSrp(), buffer);
72         }
73         serializeObject(report.getLsp(), buffer);
74         final Path p = report.getPath();
75         if (p != null) {
76             serializeObject(p.getEro(), buffer);
77             serializeObject(p.getLspa(), buffer);
78             serializeObject(p.getBandwidth(), buffer);
79             serializeObject(p.getReoptimizationBandwidth(), buffer);
80             for (final Metrics m : p.nonnullMetrics()) {
81                 serializeObject(m.getMetric(), buffer);
82             }
83             serializeObject(p.getIro(), buffer);
84             serializeObject(p.getRro(), buffer);
85         }
86     }
87
88     @Override
89     public Message validate(final Queue<Object> objects, final List<Message> errors) throws PCEPDeserializerException {
90         checkArgument(objects != null, "Passed list can't be null.");
91         if (objects.isEmpty()) {
92             throw new PCEPDeserializerException("Pcrpt message cannot be empty.");
93         }
94
95         final var reports = new ArrayList<Reports>();
96         while (!objects.isEmpty()) {
97             final Reports report = getValidReports(objects, errors);
98             if (report != null) {
99                 reports.add(report);
100             }
101         }
102         return new PcrptBuilder().setPcrptMessage(new PcrptMessageBuilder().setReports(reports).build()).build();
103     }
104
105     protected Reports getValidReports(final Queue<Object> objects, final List<Message> errors) {
106         final ReportsBuilder builder = new ReportsBuilder();
107
108         boolean lspViaSR = false;
109         Object object = objects.remove();
110         if (object instanceof Srp srp) {
111             final Tlvs tlvs = srp.getTlvs();
112             if (tlvs != null) {
113                 lspViaSR = PSTUtil.isDefaultPST(tlvs.getPathSetupType());
114             }
115             builder.setSrp(srp);
116             object = objects.poll();
117         }
118
119         if (validateLsp(object, lspViaSR, errors, builder)) {
120             if (!objects.isEmpty()) {
121                 if (!validatePath(objects, errors, builder)) {
122                     return null;
123                 }
124             }
125
126             return builder.build();
127         }
128         return null;
129     }
130
131     private static boolean validateLsp(final Object object, final boolean lspViaSR, final List<Message> errors,
132             final ReportsBuilder builder) {
133         if (object instanceof Lsp lsp) {
134             final org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.ietf.stateful.rev200720.lsp
135                 .object.lsp.Tlvs tlvs = lsp.getTlvs();
136             if (!lspViaSR && lsp.getPlspId().getValue().toJava() != 0
137                     && (tlvs == null || tlvs.getLspIdentifiers() == null)) {
138                 final Message errorMsg = createErrorMsg(PCEPErrors.LSP_IDENTIFIERS_TLV_MISSING, Optional.empty());
139                 errors.add(errorMsg);
140                 return false;
141             }
142
143             builder.setLsp(lsp);
144             return true;
145         }
146
147         errors.add(createErrorMsg(PCEPErrors.LSP_MISSING, Optional.empty()));
148         return false;
149     }
150
151     private static boolean validatePath(final Queue<Object> objects, final List<Message> errors,
152             final ReportsBuilder builder) {
153         final PathBuilder pBuilder = new PathBuilder();
154         Object object = objects.remove();
155         if (object instanceof Ero ero) {
156             pBuilder.setEro(ero);
157         } else {
158             errors.add(createErrorMsg(PCEPErrors.ERO_MISSING, Optional.empty()));
159             return false;
160         }
161         parsePath(objects, pBuilder);
162         builder.setPath(pBuilder.build());
163         return true;
164     }
165
166     private static void parsePath(final Queue<Object> objects, final PathBuilder builder) {
167         final List<Metrics> pathMetrics = new ArrayList<>();
168         State state = State.INIT;
169
170         for (Object obj = objects.peek(); obj != null; obj = objects.peek()) {
171             state = insertObject(state, obj, builder, pathMetrics);
172             if (state == State.END) {
173                 break;
174             }
175
176             objects.remove();
177         }
178         if (!pathMetrics.isEmpty()) {
179             builder.setMetrics(pathMetrics);
180         }
181     }
182
183     /**
184      * Determine the type of Object and insert it in the PathBuilder.
185      *
186      * <p>This method uses a state machine to check that Objects are seen only once and to speed up the browsing.
187      * However, the order of Object in the PcReport has changed between old draft version and final RFC8231.
188      * Indeed, as per RFC8231, the PcReport is composed of: ["SRP"], "LSP", "path"
189      * where "path" = "intended-path", ["actual-attribute-list", "actual-path"], "intended-attribute-list"
190      * and where "intended-path" = ERO, "actual-attribute-list" = BANDWIDTH, METRICS, "actual-path" = RRO
191      * and "intended-attribute-list" = LSPA, BANDWIDTH, METRICS, IRO
192      * In old draft version, "intended-attribute-list" was placed just right after the "intended-path".
193      * Thus, the state machine should be flexible enough to accommodate to PCCs that continue to use old draft and
194      * PCCs that are compliant to the RFC8231.</p>
195      *
196      * @param state         Current State of the state machine
197      * @param obj           Object to be identify and added to Path Builder
198      * @param builder       Path Builder to be fill with the Object
199      * @param pathMetrics   List of Metrics to be fill with Object when it is a Metrics
200      *
201      * @return              New State of the state machine
202      */
203     private static State insertObject(final State state, final Object obj, final PathBuilder builder,
204             final List<Metrics> pathMetrics) {
205         switch (state) {
206             case INIT:
207                 if (obj instanceof Lspa lspa) {
208                     builder.setLspa(lspa);
209                     return State.LSPA_IN;
210                 }
211                 // fall through
212             case LSPA_IN:
213                 // Check presence for <intended-attribute-list> i.e LSPA, Bandwidth, Metrics, IRO ... as per old draft
214                 if (obj instanceof Bandwidth bandwidth) {
215                     builder.setBandwidth(bandwidth);
216                     return State.LSPA_IN;
217                 }
218                 if (obj
219                         instanceof org.opendaylight.yang.gen.v1.urn.opendaylight.params.xml.ns.yang.pcep.types.rev181109
220                             .reoptimization.bandwidth.object.ReoptimizationBandwidth reoptBandwidth) {
221                     builder.setReoptimizationBandwidth(reoptBandwidth);
222                     return State.LSPA_IN;
223                 }
224                 // fall through
225             case BANDWIDTH_IN:
226                 if (obj instanceof Metric metric) {
227                     pathMetrics.add(new MetricsBuilder().setMetric(metric).build());
228                     return State.BANDWIDTH_IN;
229                 }
230                 // fall through
231             case METRIC_IN:
232                 if (obj instanceof Iro iro) {
233                     builder.setIro(iro);
234                     return State.IRO_IN;
235                 }
236                 // fall through
237             case IRO_IN:
238                 if (obj instanceof Rro rro) {
239                     builder.setRro(rro);
240                     return State.RRO_IN;
241                 }
242                 // fall through
243             case RRO_IN:
244                 // Check presence for <intended-attribute-list> i.e LSPA, Bandwidth, Metrics, IRO ... as per RFC8231
245                 if (obj instanceof Lspa lspa) {
246                     builder.setLspa(lspa);
247                     return State.LSPA_IN;
248                 }
249                 // fall through
250             case END:
251                 return State.END;
252             default:
253                 return state;
254         }
255     }
256
257     private enum State {
258         INIT, LSPA_IN, BANDWIDTH_IN, METRIC_IN, IRO_IN, RRO_IN, END
259     }
260 }