blob: 2041cdbea721233da8028d1ebe918daa7eeae78b [file] [log] [blame]
Carmelo Cascone07d72712017-07-14 15:57:47 -04001/*
Brian O'Connora09fe5b2017-08-03 21:12:30 -07002 * Copyright 2017-present Open Networking Foundation
Carmelo Cascone07d72712017-07-14 15:57:47 -04003 *
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.net.pi.model;
18
19import com.google.common.collect.ImmutableMap;
Carmelo Cascone3b4baa02019-04-17 16:39:12 -070020import com.google.common.hash.Hashing;
21import com.google.common.hash.HashingInputStream;
Carmelo Cascone07d72712017-07-14 15:57:47 -040022import org.onosproject.net.driver.Behaviour;
23
Carmelo Cascone31d3e442017-07-18 16:58:51 -040024import java.io.IOException;
Carmelo Cascone07d72712017-07-14 15:57:47 -040025import java.io.InputStream;
Carmelo Cascone31d3e442017-07-18 16:58:51 -040026import java.net.URL;
ghj0504520ec1a4202018-10-22 10:50:41 -070027import java.nio.ByteBuffer;
28import java.util.ArrayList;
29import java.util.Arrays;
Carmelo Cascone07d72712017-07-14 15:57:47 -040030import java.util.Collection;
31import java.util.Map;
32import java.util.Optional;
33
Carmelo Cascone31d3e442017-07-18 16:58:51 -040034import static com.google.common.base.Preconditions.checkArgument;
Carmelo Cascone07d72712017-07-14 15:57:47 -040035import static com.google.common.base.Preconditions.checkNotNull;
Carmelo Cascone31d3e442017-07-18 16:58:51 -040036import static java.lang.String.format;
Carmelo Cascone07d72712017-07-14 15:57:47 -040037
38/**
39 * Default pipeconf implementation.
40 */
41public final class DefaultPiPipeconf implements PiPipeconf {
42
43 private final PiPipeconfId id;
44 private final PiPipelineModel pipelineModel;
ghj0504520ec1a4202018-10-22 10:50:41 -070045 private final long fingerprint;
Carmelo Cascone07d72712017-07-14 15:57:47 -040046 private final Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours;
Carmelo Cascone31d3e442017-07-18 16:58:51 -040047 private final Map<ExtensionType, URL> extensions;
Carmelo Cascone07d72712017-07-14 15:57:47 -040048
ghj0504520ec1a4202018-10-22 10:50:41 -070049 private DefaultPiPipeconf(PiPipeconfId id, PiPipelineModel pipelineModel, long fingerprint,
Carmelo Cascone07d72712017-07-14 15:57:47 -040050 Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours,
Carmelo Cascone31d3e442017-07-18 16:58:51 -040051 Map<ExtensionType, URL> extensions) {
Carmelo Cascone07d72712017-07-14 15:57:47 -040052 this.id = id;
53 this.pipelineModel = pipelineModel;
ghj0504520ec1a4202018-10-22 10:50:41 -070054 this.fingerprint = fingerprint;
Carmelo Cascone07d72712017-07-14 15:57:47 -040055 this.behaviours = behaviours;
56 this.extensions = extensions;
57 }
58
59 @Override
60 public PiPipeconfId id() {
61 return id;
62 }
63
64 @Override
65 public PiPipelineModel pipelineModel() {
66 return pipelineModel;
67 }
68
69 @Override
ghj0504520ec1a4202018-10-22 10:50:41 -070070 public long fingerprint() {
71 return fingerprint;
72 }
73
74 @Override
Carmelo Cascone07d72712017-07-14 15:57:47 -040075 public Collection<Class<? extends Behaviour>> behaviours() {
76 return behaviours.keySet();
77 }
78
79 @Override
80 public Optional<Class<? extends Behaviour>> implementation(Class<? extends Behaviour> behaviour) {
81 return Optional.ofNullable(behaviours.get(behaviour));
82 }
83
84 @Override
85 public boolean hasBehaviour(Class<? extends Behaviour> behaviourClass) {
86 return behaviours.containsKey(behaviourClass);
87 }
88
89 @Override
90 public Optional<InputStream> extension(ExtensionType type) {
Carmelo Cascone31d3e442017-07-18 16:58:51 -040091 if (extensions.containsKey(type)) {
92 try {
93 return Optional.of(extensions.get(type).openStream());
94 } catch (IOException e) {
Ray Milkey986a47a2018-01-25 11:38:51 -080095 throw new IllegalStateException(e);
Carmelo Cascone31d3e442017-07-18 16:58:51 -040096 }
97 } else {
98 return Optional.empty();
99 }
Carmelo Cascone07d72712017-07-14 15:57:47 -0400100 }
101
102 /**
103 * Returns a new pipeconf builder.
104 *
105 * @return pipeconf builder
106 */
107 public static Builder builder() {
108 return new Builder();
109 }
110
111 /**
112 * Builder of pipeconf implementations.
113 */
114 public static class Builder {
115
116 private PiPipeconfId id;
117 private PiPipelineModel pipelineModel;
118 private ImmutableMap.Builder<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviourMapBuilder
119 = ImmutableMap.builder();
Carmelo Cascone31d3e442017-07-18 16:58:51 -0400120 private ImmutableMap.Builder<ExtensionType, URL> extensionMapBuilder = ImmutableMap.builder();
Carmelo Cascone07d72712017-07-14 15:57:47 -0400121
122 /**
123 * Sets the identifier of this pipeconf.
124 *
125 * @param id pipeconf identifier
126 * @return this
127 */
128 public Builder withId(PiPipeconfId id) {
129 this.id = id;
130 return this;
131 }
132
133 /**
134 * Sets the pipeline model of this pipeconf.
135 *
136 * @param model pipeline model
137 * @return this
138 */
139 public Builder withPipelineModel(PiPipelineModel model) {
140 this.pipelineModel = model;
141 return this;
142 }
143
144 /**
145 * Adds a behaviour to this pipeconf.
146 *
147 * @param clazz behavior interface class
148 * @param implementation behavior implementation class
149 * @return this
150 */
151 public Builder addBehaviour(Class<? extends Behaviour> clazz, Class<? extends Behaviour> implementation) {
152 checkNotNull(clazz);
153 checkNotNull(implementation);
154 behaviourMapBuilder.put(clazz, implementation);
155 return this;
156 }
157
158 /**
159 * Adds an extension to this pipeconf.
160 *
Carmelo Cascone31d3e442017-07-18 16:58:51 -0400161 * @param type extension type
162 * @param url url pointing at the extension file
Carmelo Cascone07d72712017-07-14 15:57:47 -0400163 * @return this
164 */
Carmelo Cascone31d3e442017-07-18 16:58:51 -0400165 public Builder addExtension(ExtensionType type, URL url) {
Carmelo Cascone07d72712017-07-14 15:57:47 -0400166 checkNotNull(type);
Carmelo Cascone31d3e442017-07-18 16:58:51 -0400167 checkNotNull(url);
168 checkArgument(checkUrl(url), format("Extension url %s seems to be empty/non existent", url.toString()));
169 extensionMapBuilder.put(type, url);
Carmelo Cascone07d72712017-07-14 15:57:47 -0400170 return this;
171 }
172
Carmelo Cascone31d3e442017-07-18 16:58:51 -0400173 private boolean checkUrl(URL url) {
174 try {
175 int byteCount = url.openStream().available();
176 return byteCount > 0;
177 } catch (IOException e) {
178 return false;
179 }
180 }
181
Carmelo Cascone07d72712017-07-14 15:57:47 -0400182 /**
183 * Creates a new pipeconf.
184 *
185 * @return pipeconf instance
186 */
187 public PiPipeconf build() {
188 checkNotNull(id);
189 checkNotNull(pipelineModel);
ghj0504520ec1a4202018-10-22 10:50:41 -0700190
191 Map<ExtensionType, URL> extensions = extensionMapBuilder.build();
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700192 Map<Class<? extends Behaviour>, Class<? extends Behaviour>> behaviours =
193 behaviourMapBuilder.build();
194 return new DefaultPiPipeconf(
pierventre4d2a5422021-01-10 17:29:03 -0800195 id, pipelineModel, generateFingerprint(extensions),
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700196 behaviours, extensions);
Carmelo Cascone07d72712017-07-14 15:57:47 -0400197 }
198
pierventre4d2a5422021-01-10 17:29:03 -0800199 private long generateFingerprint(Map<ExtensionType, URL> extensions) {
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700200 Collection<Integer> hashes = new ArrayList<>();
pierventre4d2a5422021-01-10 17:29:03 -0800201 for (ExtensionType extensionType : ExtensionType.values()) {
ghj0504520ec1a4202018-10-22 10:50:41 -0700202 try {
pierventre4d2a5422021-01-10 17:29:03 -0800203 // Get the extension if present and then hash the content
204 URL extUrl = extensions.get(extensionType);
205 if (extUrl != null) {
206 HashingInputStream hin = new HashingInputStream(
207 Hashing.crc32(), extUrl.openStream());
208 //noinspection StatementWithEmptyBody
209 while (hin.read() != -1) {
210 // Do nothing. Reading all input stream to update hash.
211 }
212 hashes.add(hin.hash().asInt());
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700213 }
ghj0504520ec1a4202018-10-22 10:50:41 -0700214 } catch (IOException e) {
215 throw new IllegalArgumentException(e);
216 }
217 }
Carmelo Cascone3b4baa02019-04-17 16:39:12 -0700218 // FIXME: how to include behaviours in the hash?
219 int low = Arrays.hashCode(hashes.toArray());
220 int high = pipelineModel.hashCode();
221 return ByteBuffer.allocate(8).putInt(high).putInt(low).getLong(0);
ghj0504520ec1a4202018-10-22 10:50:41 -0700222 }
Carmelo Cascone07d72712017-07-14 15:57:47 -0400223 }
224}