Fixup Augmentable and Identifiable methods changing
[openflowplugin.git] / openflowplugin-impl / src / main / java / org / opendaylight / openflowplugin / impl / protocol / serialization / messages / FlowMessageSerializer.java
1 /*
2  * Copyright (c) 2016 Pantheon Technologies s.r.o. 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
9 package org.opendaylight.openflowplugin.impl.protocol.serialization.messages;
10
11 import com.google.common.base.MoreObjects;
12 import com.google.common.base.Preconditions;
13 import io.netty.buffer.ByteBuf;
14 import java.math.BigInteger;
15 import java.util.ArrayList;
16 import java.util.Collection;
17 import java.util.List;
18 import java.util.Objects;
19 import java.util.Optional;
20 import java.util.stream.Collectors;
21 import org.opendaylight.openflowjava.protocol.api.extensibility.OFSerializer;
22 import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistry;
23 import org.opendaylight.openflowjava.protocol.api.extensibility.SerializerRegistryInjector;
24 import org.opendaylight.openflowjava.protocol.api.keys.MessageTypeKey;
25 import org.opendaylight.openflowjava.protocol.api.util.EncodeConstants;
26 import org.opendaylight.openflowjava.util.ByteBufUtils;
27 import org.opendaylight.openflowplugin.api.OFConstants;
28 import org.opendaylight.openflowplugin.impl.protocol.serialization.util.InstructionUtil;
29 import org.opendaylight.openflowplugin.openflow.md.core.sal.convertor.common.OrderComparator;
30 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.ActionList;
31 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.VlanCfi;
32 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCaseBuilder;
33 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCase;
34 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpDstActionCaseBuilder;
35 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCase;
36 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetTpSrcActionCaseBuilder;
37 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase;
38 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.push.vlan.action._case.PushVlanActionBuilder;
39 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.dst.action._case.SetTpDstActionBuilder;
40 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.set.tp.src.action._case.SetTpSrcActionBuilder;
41 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.Action;
42 import org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.list.ActionBuilder;
43 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.Flow;
44 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowCookie;
45 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessage;
46 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowMessageBuilder;
47 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags;
48 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.InstructionsBuilder;
49 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.flow.MatchBuilder;
50 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.Instruction;
51 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCase;
52 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.ApplyActionsCaseBuilder;
53 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.instruction.apply.actions._case.ApplyActionsBuilder;
54 import org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list.InstructionBuilder;
55 import org.opendaylight.yang.gen.v1.urn.opendaylight.l2.types.rev130827.VlanId;
56 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.Match;
57 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatch;
58 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.match.VlanMatchBuilder;
59 import org.opendaylight.yang.gen.v1.urn.opendaylight.model.match.types.rev131026.vlan.match.fields.VlanIdBuilder;
60
61 /**
62  * Translates FlowMod messages.
63  * OF protocol versions: 1.3
64  */
65 public class FlowMessageSerializer extends AbstractMessageSerializer<FlowMessage> implements
66         SerializerRegistryInjector {
67     private static final FlowCookie DEFAULT_COOKIE = new FlowCookie(OFConstants.DEFAULT_COOKIE);
68     private static final FlowCookie DEFAULT_COOKIE_MASK = new FlowCookie(OFConstants.DEFAULT_COOKIE_MASK);
69     private static final Short DEFAULT_TABLE_ID = (short) 0;
70     private static final Integer DEFAULT_IDLE_TIMEOUT = 0;
71     private static final Integer DEFAULT_HARD_TIMEOUT = 0;
72     private static final Integer DEFAULT_PRIORITY = OFConstants.DEFAULT_FLOW_PRIORITY;
73     private static final Long DEFAULT_BUFFER_ID = OFConstants.OFP_NO_BUFFER;
74     private static final BigInteger DEFAULT_OUT_PORT = BigInteger.valueOf(OFConstants.OFPP_ANY);
75     private static final Long DEFAULT_OUT_GROUP = OFConstants.OFPG_ANY;
76     private static final byte PADDING_IN_FLOW_MOD_MESSAGE = 2;
77     private static final FlowModFlags DEFAULT_FLAGS = new FlowModFlags(false, false, false, false, false);
78     private static final Integer PUSH_VLAN = 0x8100;
79
80     private static final VlanMatch VLAN_MATCH_FALSE = new VlanMatchBuilder()
81             .setVlanId(new VlanIdBuilder()
82                     .setVlanIdPresent(false)
83                     .setVlanId(new VlanId(0))
84                     .build())
85             .build();
86
87     private static final VlanMatch VLAN_MATCH_TRUE = new VlanMatchBuilder()
88             .setVlanId(new VlanIdBuilder()
89                     .setVlanIdPresent(true)
90                     .setVlanId(new VlanId(0))
91                     .build())
92             .build();
93
94     private SerializerRegistry registry;
95
96     @Override
97     public void serialize(FlowMessage message, ByteBuf outBuffer) {
98         if (!isVlanMatchPresent(message) && isSetVlanIdActionCasePresent(message)) {
99             writeVlanFlow(message, outBuffer);
100         } else {
101             writeFlow(message, outBuffer);
102         }
103     }
104
105     /**
106      * Serialize flow message. Needs to be separated from main serialize method to prevent recursion
107      * when serializing SetVlanId flows.
108      *
109      * @param message   flow message
110      * @param outBuffer output buffer
111      */
112     private void writeFlow(final FlowMessage message, final ByteBuf outBuffer) {
113         final int index = outBuffer.writerIndex();
114         super.serialize(message, outBuffer);
115         outBuffer.writeLong(MoreObjects.firstNonNull(message.getCookie(), DEFAULT_COOKIE).getValue().longValue());
116         outBuffer.writeLong(MoreObjects.firstNonNull(message.getCookieMask(), DEFAULT_COOKIE_MASK).getValue()
117                 .longValue());
118         outBuffer.writeByte(MoreObjects.firstNonNull(message.getTableId(), DEFAULT_TABLE_ID));
119         outBuffer.writeByte(message.getCommand().getIntValue());
120         outBuffer.writeShort(MoreObjects.firstNonNull(message.getIdleTimeout(), DEFAULT_IDLE_TIMEOUT));
121         outBuffer.writeShort(MoreObjects.firstNonNull(message.getHardTimeout(), DEFAULT_HARD_TIMEOUT));
122         outBuffer.writeShort(MoreObjects.firstNonNull(message.getPriority(), DEFAULT_PRIORITY));
123         outBuffer.writeInt(MoreObjects.firstNonNull(message.getBufferId(), DEFAULT_BUFFER_ID).intValue());
124         outBuffer.writeInt(MoreObjects.firstNonNull(message.getOutPort(), DEFAULT_OUT_PORT).intValue());
125         outBuffer.writeInt(MoreObjects.firstNonNull(message.getOutGroup(), DEFAULT_OUT_GROUP).intValue());
126         outBuffer.writeShort(createFlowModFlagsBitmask(MoreObjects.firstNonNull(message.getFlags(), DEFAULT_FLAGS)));
127         outBuffer.writeZero(PADDING_IN_FLOW_MOD_MESSAGE);
128         writeMatch(message, outBuffer);
129         writeInstructions(message, outBuffer);
130         outBuffer.setShort(index + 2, outBuffer.writerIndex() - index);
131     }
132
133     /**
134      * Instead of serializing this flow normally, we need to split it to two parts if flow contains
135      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}.
136      *
137      * @param message   flow mod message
138      * @param outBuffer output buffer
139      */
140     private void writeVlanFlow(final FlowMessage message, final ByteBuf outBuffer) {
141         writeFlow(
142                 new FlowMessageBuilder(message)
143                         .setMatch(new MatchBuilder(message.getMatch())
144                                 .setVlanMatch(VLAN_MATCH_FALSE)
145                                 .build())
146                         .setInstructions(new InstructionsBuilder()
147                                 .setInstruction(updateSetVlanIdAction(message))
148                                 .build())
149                         .build(),
150                 outBuffer);
151
152         writeFlow(
153                 new FlowMessageBuilder(message)
154                         .setMatch(new MatchBuilder(message.getMatch())
155                                 .setVlanMatch(VLAN_MATCH_TRUE)
156                                 .build())
157                         .build(),
158                 outBuffer);
159     }
160
161     /**
162      * Serialize OpenFlowPlugin match to raw bytes.
163      *
164      * @param message   OpenFlow flow mod message
165      * @param outBuffer output buffer
166      */
167     private void writeMatch(final FlowMessage message, final ByteBuf outBuffer) {
168         Preconditions.checkNotNull(registry).<Match, OFSerializer<Match>>getSerializer(
169                 new MessageTypeKey<>(message.getVersion(), Match.class)).serialize(message.getMatch(), outBuffer);
170
171     }
172
173     /**
174      * Serialize OpenFlowPlugin instructions and set ip protocol of set-tp-src and set-tp-dst actions of need.
175      *
176      * @param message   OpenFlow flow mod message
177      * @param outBuffer output buffer
178      */
179     @SuppressWarnings("unchecked")
180     private void writeInstructions(final FlowMessage message, final ByteBuf outBuffer) {
181         // Try to get IP protocol from IP match
182         final Optional<Short> protocol = Optional
183                 .ofNullable(message.getMatch())
184                 .flatMap(m -> Optional.ofNullable(m.getIpMatch()))
185                 .flatMap(ipm -> Optional.ofNullable(ipm.getIpProtocol()));
186
187         // Update instructions if needed and then serialize all instructions
188         Optional.ofNullable(message.getInstructions())
189                 .flatMap(is -> Optional.ofNullable(is.getInstruction()))
190                 .ifPresent(is -> is
191                         .stream()
192                         .filter(Objects::nonNull)
193                         .sorted(OrderComparator.build())
194                         .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
195                                 .rev131026.Instruction::getInstruction)
196                         .filter(Objects::nonNull)
197                         .map(i -> protocol.flatMap(p -> updateInstruction(i, p)).orElse(i))
198                         .forEach(i -> InstructionUtil.writeInstruction(i, EncodeConstants.OF13_VERSION_ID, registry,
199                                 outBuffer)));
200     }
201
202     /**
203      * Determine type of instruction and update it's actions if it is apply-actions instruction.
204      *
205      * @param instruction instruction
206      * @param protocol    protocol
207      * @return updated instruction or empty
208      */
209     private static Optional<Instruction> updateInstruction(final Instruction instruction, final Short protocol) {
210         if (ApplyActionsCase.class.isInstance(instruction)) {
211             return Optional
212                     .ofNullable(ApplyActionsCase.class.cast(instruction).getApplyActions())
213                     .flatMap(aa -> Optional.ofNullable(aa.getAction()))
214                     .map(as -> new ApplyActionsCaseBuilder()
215                             .setApplyActions(new ApplyActionsBuilder()
216                                     .setAction(as
217                                             .stream()
218                                             .filter(Objects::nonNull)
219                                             .map(a -> updateSetTpActions(a, protocol))
220                                             .collect(Collectors.toList()))
221                                     .build())
222                             .build());
223         }
224
225         return Optional.empty();
226     }
227
228     /**
229      * If action is set-tp-src or set-tp-dst, inject IP protocol into it, otherwise return original action.
230      *
231      * @param action   OpenFlow action
232      * @param protocol IP protocol
233      * @return updated OpenFlow action
234      */
235     private static Action updateSetTpActions(Action action, Short protocol) {
236         if (SetTpSrcActionCase.class.isInstance(action.getAction())) {
237             final SetTpSrcActionCase actionCase = SetTpSrcActionCase.class.cast(action.getAction());
238
239             return new ActionBuilder(action)
240                     .setAction(new SetTpSrcActionCaseBuilder(actionCase)
241                             .setSetTpSrcAction(new SetTpSrcActionBuilder(
242                                     actionCase.getSetTpSrcAction())
243                                     .setIpProtocol(protocol)
244                                     .build())
245                             .build())
246                     .build();
247         } else if (SetTpDstActionCase.class.isInstance(action.getAction())) {
248             final SetTpDstActionCase actionCase = SetTpDstActionCase.class.cast(action.getAction());
249
250             return new ActionBuilder(action)
251                     .setAction(new SetTpDstActionCaseBuilder(actionCase)
252                             .setSetTpDstAction(new SetTpDstActionBuilder(
253                                     actionCase.getSetTpDstAction())
254                                     .setIpProtocol(protocol)
255                                     .build())
256                             .build())
257                     .build();
258         }
259
260         // Return original action if no modifications are needed
261         return action;
262     }
263
264     /**
265      * Create copy of instructions of original flow but insert
266      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.PushVlanActionCase}
267      * before each
268      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}.
269      *
270      * @param message OpenFlowPlugin flow mod message
271      * @return list of instructions
272      */
273     private static List<org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.instruction.list
274             .Instruction> updateSetVlanIdAction(final FlowMessage message) {
275         return message.getInstructions().getInstruction()
276                 .stream()
277                 .map(i -> {
278                     final int[] offset = {0};
279
280                     return ApplyActionsCase.class.isInstance(i.getInstruction())
281                             ? Optional
282                             .ofNullable(ApplyActionsCase.class.cast(i.getInstruction()).getApplyActions())
283                             .flatMap(as -> Optional.ofNullable(as.getAction()))
284                             .map(a -> a.stream()
285                                     .sorted(OrderComparator.build())
286                                     .flatMap(action -> {
287                                         final List<org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112
288                                                 .action.list.Action> actions = new ArrayList<>();
289
290                                         // If current action is SetVlanId, insert PushVlan action before it and
291                                         // update order
292                                         if (SetVlanIdActionCase.class.isInstance(action.getAction())) {
293                                             actions.add(new ActionBuilder()
294                                                     .setAction(new PushVlanActionCaseBuilder()
295                                                             .setPushVlanAction(new PushVlanActionBuilder()
296                                                                     .setCfi(new VlanCfi(1))
297                                                                     .setVlanId(SetVlanIdActionCase.class.cast(action
298                                                                             .getAction()).getSetVlanIdAction()
299                                                                             .getVlanId())
300                                                                     .setEthernetType(PUSH_VLAN)
301                                                                     .setTag(PUSH_VLAN)
302                                                                     .build())
303                                                             .build())
304                                                     .withKey(action.key())
305                                                     .setOrder(action.getOrder() + offset[0])
306                                                     .build());
307
308                                             offset[0]++;
309                                         }
310
311                                         // Update offset of action if there is any inserted PushVlan actions
312                                         actions.add(offset[0] > 0
313                                                 ? new ActionBuilder(action).setOrder(action.getOrder() + offset[0])
314                                                 .build()
315                                                 : action);
316
317                                         return actions.stream();
318                                     }))
319                             .map(as -> new InstructionBuilder(i)
320                                     .setInstruction(new ApplyActionsCaseBuilder()
321                                             .setApplyActions(new ApplyActionsBuilder()
322                                                     .setAction(as.collect(Collectors.toList()))
323                                                     .build())
324                                             .build())
325                                     .build())
326                             .orElse(i)
327                             : i;
328                 }).collect(Collectors.toList());
329     }
330
331     /**
332      * Create integer bit mask from
333      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types.rev131026.FlowModFlags}.
334      *
335      * @param flags flow mod flags
336      * @return bit mask
337      */
338     private static int createFlowModFlagsBitmask(final FlowModFlags flags) {
339         return ByteBufUtils.fillBitMask(0,
340                 flags.isSENDFLOWREM(),
341                 flags.isCHECKOVERLAP(),
342                 flags.isRESETCOUNTS(),
343                 flags.isNOPKTCOUNTS(),
344                 flags.isNOBYTCOUNTS());
345     }
346
347     /**
348      * Determine if flow contains vlan match.
349      *
350      * @param flow flow
351      * @return true if flow contains vlan match
352      */
353     private static boolean isVlanMatchPresent(final Flow flow) {
354         return Optional
355                 .ofNullable(flow.getMatch())
356                 .flatMap(m -> Optional.ofNullable(m.getVlanMatch()))
357                 .isPresent();
358     }
359
360     /**
361      * Determine if flow contains
362      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight
363      * .flow.types.rev131026.instruction.instruction.ApplyActionsCase} instruction with
364      * #{@link org.opendaylight.yang.gen.v1.urn.opendaylight.action.types.rev131112.action.action.SetVlanIdActionCase}
365      * action.
366      *
367      * @param flow OpenFlowPlugin flow
368      * @return true if flow contains SetVlanIdAction
369      */
370     private static boolean isSetVlanIdActionCasePresent(final Flow flow) {
371         return Optional
372                 .ofNullable(flow.getInstructions())
373                 .flatMap(is -> Optional.ofNullable(is.getInstruction()))
374                 .flatMap(is -> is
375                         .stream()
376                         .map(org.opendaylight.yang.gen.v1.urn.opendaylight.flow.types
377                                 .rev131026.Instruction::getInstruction)
378                         .filter(ApplyActionsCase.class::isInstance)
379                         .map(i -> ApplyActionsCase.class.cast(i).getApplyActions())
380                         .filter(Objects::nonNull)
381                         .map(ActionList::getAction)
382                         .filter(Objects::nonNull)
383                         .flatMap(Collection::stream)
384                         .map(Action::getAction)
385                         .filter(SetVlanIdActionCase.class::isInstance)
386                         .findFirst())
387                 .isPresent();
388     }
389
390     @Override
391     protected byte getMessageType() {
392         return 14;
393     }
394
395     @Override
396     public void injectSerializerRegistry(SerializerRegistry serializerRegistry) {
397         registry = serializerRegistry;
398     }
399 }