blob: 731b2cacf82cd651af4ebaaa6a571f56b58c5f76 [file] [log] [blame]
Carmelo Casconeefc0a922016-06-14 14:32:33 -07001/*
2 * Copyright 2016-present Open Networking Laboratory
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package org.onosproject.bmv2.demo.app.wcmp;
18
19import com.google.common.collect.ImmutableList;
20import com.google.common.collect.Lists;
21import com.google.common.collect.Maps;
22import org.onlab.util.ImmutableByteSequence;
23import org.onosproject.bmv2.api.runtime.Bmv2Action;
24import org.onosproject.bmv2.api.runtime.Bmv2ExtensionTreatment;
25import org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils;
26import org.onosproject.net.DeviceId;
27import org.onosproject.net.PortNumber;
28import org.onosproject.net.flow.instructions.ExtensionTreatment;
29
30import java.util.Collections;
31import java.util.List;
32import java.util.Map;
33
34import static com.google.common.base.Preconditions.checkArgument;
35import static java.util.stream.Collectors.toList;
36import static org.onosproject.bmv2.api.utils.Bmv2TranslatorUtils.fitByteSequence;
37import static org.onosproject.bmv2.demo.app.wcmp.WcmpFabricApp.WCMP_CONTEXT;
38import static org.onosproject.bmv2.demo.app.wcmp.WcmpInterpreter.*;
39
40/**
41 * Builder of WCMP extension treatment.
42 */
43public final class WcmpGroupTreatmentBuilder {
44
45 private static final double MAX_ERROR = 0.0001;
46
47 private static final Map<DeviceId, Map<Map<PortNumber, Double>, Integer>> DEVICE_GROUP_ID_MAP = Maps.newHashMap();
48
49 private int groupId;
50
51 /**
52 * Sets the WCMP group ID.
53 *
54 * @param groupId an integer value
55 * @return this
56 */
57 public WcmpGroupTreatmentBuilder withGroupId(int groupId) {
58 this.groupId = groupId;
59 return this;
60 }
61
62 /**
63 * Returns a new extension treatment.
64 *
65 * @return an extension treatment
66 */
67 public ExtensionTreatment build() {
68 checkArgument(groupId >= 0, "group id must be a non-zero positive integer");
69 ImmutableByteSequence groupIdBs = ImmutableByteSequence.copyFrom(groupId);
70 final int groupIdBitWidth = WCMP_CONTEXT.configuration().headerType(WCMP_META_T).field(GROUP_ID).bitWidth();
71 try {
72 groupIdBs = fitByteSequence(groupIdBs, groupIdBitWidth);
73 return new Bmv2ExtensionTreatment(
74 Bmv2Action.builder()
75 .withName(WCMP_GROUP)
76 .addParameter(groupIdBs)
77 .build());
78 } catch (Bmv2TranslatorUtils.ByteSequenceFitException e) {
79 throw new RuntimeException(e);
80 }
81 }
82
83 public static int groupIdOf(DeviceId did, Map<PortNumber, Double> weightedPorts) {
84 DEVICE_GROUP_ID_MAP.putIfAbsent(did, Maps.newHashMap());
85 // Counts the number of unique portNumber sets for each device ID.
86 // Each distinct set of portNumbers will have a unique ID.
87 return DEVICE_GROUP_ID_MAP.get(did).computeIfAbsent(weightedPorts,
88 (pp) -> DEVICE_GROUP_ID_MAP.get(did).size() + 1);
89 }
90
91 public static List<Integer> toPrefixLengths(List<Double> weigths) throws WcmpGroupException {
92
93 double weightSum = weigths.stream()
94 .mapToDouble(Double::doubleValue)
95 .map(WcmpGroupTreatmentBuilder::roundDouble)
96 .sum();
97
98 if (Math.abs(weightSum - 1) > MAX_ERROR) {
99 throw new WcmpGroupException("weights sum is expected to be 1, found was " + weightSum);
100 }
101
102 final int selectorBitWidth = WCMP_CONTEXT.configuration().headerType(WCMP_META_T).field(SELECTOR).bitWidth();
103 final int availableBits = selectorBitWidth - 1;
104
105 List<Long> prefixDiffs = weigths.stream().map(w -> Math.round(w * availableBits)).collect(toList());
106
107 final long bitSum = prefixDiffs.stream().mapToLong(Long::longValue).sum();
108 final long error = availableBits - bitSum;
109
110 if (error != 0) {
111 // Lazy intuition here is that the error can be absorbed by the longest prefixDiff with the minor impact.
112 Long maxDiff = Collections.max(prefixDiffs);
113 int idx = prefixDiffs.indexOf(maxDiff);
114 prefixDiffs.remove(idx);
115 prefixDiffs.add(idx, maxDiff + error);
116 }
117 List<Integer> prefixLengths = Lists.newArrayList();
118
119 int prefix = 1;
120 for (Long p : prefixDiffs) {
121 prefixLengths.add(prefix);
122 prefix += p;
123 }
124 return ImmutableList.copyOf(prefixLengths);
125 }
126
127 private static double roundDouble(double n) {
128 // 5 digits precision.
129 return (double) Math.round(n * 100000d) / 100000d;
130 }
131
132 public static class WcmpGroupException extends Exception {
133 public WcmpGroupException(String s) {
134 }
135 }
136}