Initial sketch and implementaion of Objectives.
These objective will be used to provide a layer of abstraction that isolates applications for device pipeline details. initial objective support Change-Id: I019302822421f0fe1f508f2f7527d91578e30116 initial implemetation of a simple pipeline behaviour Change-Id: Id0d9167896c717d05cda90e1524fc24c76e9fc9b initial implementation of objectives Change-Id: I768fa2020308d5feb95eaaff355aa511b323beca
Showing
12 changed files
with
1238 additions
and
0 deletions
... | @@ -25,6 +25,12 @@ public class ControllerInfo { | ... | @@ -25,6 +25,12 @@ public class ControllerInfo { |
25 | public final IpAddress ip; | 25 | public final IpAddress ip; |
26 | public final int tcpPort; | 26 | public final int tcpPort; |
27 | 27 | ||
28 | + /** | ||
29 | + * Information for contacting the controller. | ||
30 | + * | ||
31 | + * @param ip the ip address | ||
32 | + * @param tcpPort the tcp port | ||
33 | + */ | ||
28 | public ControllerInfo(IpAddress ip, int tcpPort) { | 34 | public ControllerInfo(IpAddress ip, int tcpPort) { |
29 | this.ip = ip; | 35 | this.ip = ip; |
30 | this.tcpPort = tcpPort; | 36 | this.tcpPort = tcpPort; | ... | ... |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.net.behaviour; | ||
17 | + | ||
18 | +import org.onlab.osgi.ServiceDirectory; | ||
19 | +import org.onosproject.net.DeviceId; | ||
20 | +import org.onosproject.net.flowobjective.FilteringObjective; | ||
21 | +import org.onosproject.net.flowobjective.ForwardingObjective; | ||
22 | + | ||
23 | +import java.util.Collection; | ||
24 | +import java.util.concurrent.Future; | ||
25 | + | ||
26 | +/** | ||
27 | + * Behaviour for handling various pipelines. | ||
28 | + */ | ||
29 | +public interface Pipeliner { | ||
30 | + | ||
31 | + /** | ||
32 | + * Injecting the service directory into the driver. | ||
33 | + * | ||
34 | + * @param deviceId the deviceId | ||
35 | + * @param serviceDirectory the service directory. | ||
36 | + */ | ||
37 | + void init(DeviceId deviceId, ServiceDirectory serviceDirectory); | ||
38 | + | ||
39 | + /** | ||
40 | + * Installs the filtering rules onto the device. | ||
41 | + * | ||
42 | + * @param filters the collection of filters | ||
43 | + * @return a future indicating the success of the operation | ||
44 | + */ | ||
45 | + Future<Boolean> filter(Collection<FilteringObjective> filters); | ||
46 | + | ||
47 | + /** | ||
48 | + * Installs the forwarding rules onto the device. | ||
49 | + * | ||
50 | + * @param forwardings the collection of forwarding objectives | ||
51 | + * @return a future indicating the success of the operation | ||
52 | + */ | ||
53 | + Future<Boolean> forward(Collection<ForwardingObjective> forwardings); | ||
54 | + | ||
55 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.net.flowobjective; | ||
17 | + | ||
18 | +import com.google.common.collect.ImmutableList; | ||
19 | +import org.onosproject.core.ApplicationId; | ||
20 | +import org.onosproject.net.flow.criteria.Criterion; | ||
21 | + | ||
22 | +import java.util.Collection; | ||
23 | +import java.util.List; | ||
24 | +import java.util.Objects; | ||
25 | + | ||
26 | +import static com.google.common.base.Preconditions.checkArgument; | ||
27 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
28 | + | ||
29 | +/** | ||
30 | + * Default implementation of a filtering objective. | ||
31 | + */ | ||
32 | +public final class DefaultFilteringObjective implements FilteringObjective { | ||
33 | + | ||
34 | + | ||
35 | + private final Criterion key; | ||
36 | + private final boolean permanent; | ||
37 | + private final int timeout; | ||
38 | + private final ApplicationId appId; | ||
39 | + private final int priority; | ||
40 | + private final List<Criterion> conditions; | ||
41 | + private final int id; | ||
42 | + private final Operation op; | ||
43 | + | ||
44 | + private DefaultFilteringObjective(Criterion key, boolean permanent, int timeout, | ||
45 | + ApplicationId appId, int priority, | ||
46 | + List<Criterion> conditions, Operation op) { | ||
47 | + this.key = key; | ||
48 | + this.permanent = permanent; | ||
49 | + this.timeout = timeout; | ||
50 | + this.appId = appId; | ||
51 | + this.priority = priority; | ||
52 | + this.conditions = conditions; | ||
53 | + this.op = op; | ||
54 | + | ||
55 | + this.id = Objects.hash(key, conditions, permanent, | ||
56 | + timeout, appId, priority); | ||
57 | + } | ||
58 | + | ||
59 | + | ||
60 | + @Override | ||
61 | + public Criterion key() { | ||
62 | + return key; | ||
63 | + } | ||
64 | + | ||
65 | + @Override | ||
66 | + public Collection<Criterion> conditions() { | ||
67 | + return conditions; | ||
68 | + } | ||
69 | + | ||
70 | + @Override | ||
71 | + public int id() { | ||
72 | + return id; | ||
73 | + } | ||
74 | + | ||
75 | + @Override | ||
76 | + public int priority() { | ||
77 | + return priority; | ||
78 | + } | ||
79 | + | ||
80 | + @Override | ||
81 | + public ApplicationId appId() { | ||
82 | + return appId; | ||
83 | + } | ||
84 | + | ||
85 | + @Override | ||
86 | + public int timeout() { | ||
87 | + return timeout; | ||
88 | + } | ||
89 | + | ||
90 | + @Override | ||
91 | + public boolean permanent() { | ||
92 | + return permanent; | ||
93 | + } | ||
94 | + | ||
95 | + @Override | ||
96 | + public Operation op() { | ||
97 | + return op; | ||
98 | + } | ||
99 | + | ||
100 | + /** | ||
101 | + * Returns a new builder. | ||
102 | + * | ||
103 | + * @return new builder | ||
104 | + */ | ||
105 | + public static Builder builder() { | ||
106 | + return new Builder(); | ||
107 | + } | ||
108 | + | ||
109 | + | ||
110 | + public static final class Builder implements FilteringObjective.Builder { | ||
111 | + private final ImmutableList.Builder<Criterion> listBuilder | ||
112 | + = ImmutableList.builder(); | ||
113 | + | ||
114 | + private Criterion key; | ||
115 | + private boolean permanent = DEFAULT_PERMANENT; | ||
116 | + private int timeout = DEFAULT_TIMEOUT; | ||
117 | + private ApplicationId appId; | ||
118 | + private int priority = DEFAULT_PRIORITY; | ||
119 | + | ||
120 | + @Override | ||
121 | + public Builder addCondition(Criterion criterion) { | ||
122 | + listBuilder.add(criterion); | ||
123 | + return this; | ||
124 | + } | ||
125 | + | ||
126 | + @Override | ||
127 | + public Builder withKey(Criterion criterion) { | ||
128 | + key = criterion; | ||
129 | + return this; | ||
130 | + } | ||
131 | + | ||
132 | + @Override | ||
133 | + public Builder makeTemporary(int timeout) { | ||
134 | + this.timeout = timeout; | ||
135 | + permanent = false; | ||
136 | + return this; | ||
137 | + } | ||
138 | + | ||
139 | + @Override | ||
140 | + public Builder makePermanent() { | ||
141 | + permanent = true; | ||
142 | + return this; | ||
143 | + } | ||
144 | + | ||
145 | + @Override | ||
146 | + public Builder fromApp(ApplicationId appId) { | ||
147 | + this.appId = appId; | ||
148 | + return this; | ||
149 | + } | ||
150 | + | ||
151 | + @Override | ||
152 | + public Builder withPriority(int priority) { | ||
153 | + this.priority = priority; | ||
154 | + return this; | ||
155 | + } | ||
156 | + | ||
157 | + @Override | ||
158 | + public FilteringObjective add() { | ||
159 | + List<Criterion> conditions = listBuilder.build(); | ||
160 | + checkNotNull(key, "Must have a key."); | ||
161 | + checkArgument(!conditions.isEmpty(), "Must have at least one condition."); | ||
162 | + checkNotNull(appId, "Must supply an application id"); | ||
163 | + | ||
164 | + return new DefaultFilteringObjective(key, permanent, timeout, | ||
165 | + appId, priority, conditions, | ||
166 | + Operation.ADD); | ||
167 | + | ||
168 | + } | ||
169 | + | ||
170 | + @Override | ||
171 | + public FilteringObjective remove() { | ||
172 | + List<Criterion> conditions = listBuilder.build(); | ||
173 | + checkNotNull(key, "Must have a key."); | ||
174 | + checkArgument(!conditions.isEmpty(), "Must have at least one condition."); | ||
175 | + checkNotNull(appId, "Must supply an application id"); | ||
176 | + | ||
177 | + return new DefaultFilteringObjective(key, permanent, timeout, | ||
178 | + appId, priority, conditions, | ||
179 | + Operation.REMOVE); | ||
180 | + | ||
181 | + } | ||
182 | + | ||
183 | + | ||
184 | + } | ||
185 | + | ||
186 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.net.flowobjective; | ||
17 | + | ||
18 | +import org.onosproject.core.ApplicationId; | ||
19 | +import org.onosproject.net.flow.TrafficSelector; | ||
20 | +import org.onosproject.net.flow.TrafficTreatment; | ||
21 | + | ||
22 | +import java.util.Objects; | ||
23 | + | ||
24 | +import static com.google.common.base.Preconditions.checkArgument; | ||
25 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
26 | + | ||
27 | +/** | ||
28 | + * Default implementation of a forwarding objective. | ||
29 | + */ | ||
30 | +public final class DefaultForwardingObjective implements ForwardingObjective { | ||
31 | + | ||
32 | + private final TrafficSelector selector; | ||
33 | + private final Flag flag; | ||
34 | + private final boolean permanent; | ||
35 | + private final int timeout; | ||
36 | + private final ApplicationId appId; | ||
37 | + private final int priority; | ||
38 | + private final int nextId; | ||
39 | + private final TrafficTreatment treatment; | ||
40 | + private final Operation op; | ||
41 | + | ||
42 | + private final int id; | ||
43 | + | ||
44 | + private DefaultForwardingObjective(TrafficSelector selector, | ||
45 | + Flag flag, boolean permanent, | ||
46 | + int timeout, ApplicationId appId, | ||
47 | + int priority, int nextId, | ||
48 | + TrafficTreatment treatment, Operation op) { | ||
49 | + this.selector = selector; | ||
50 | + this.flag = flag; | ||
51 | + this.permanent = permanent; | ||
52 | + this.timeout = timeout; | ||
53 | + this.appId = appId; | ||
54 | + this.priority = priority; | ||
55 | + this.nextId = nextId; | ||
56 | + this.treatment = treatment; | ||
57 | + this.op = op; | ||
58 | + | ||
59 | + this.id = Objects.hash(selector, flag, permanent, | ||
60 | + timeout, appId, priority, nextId, | ||
61 | + treatment, op); | ||
62 | + } | ||
63 | + | ||
64 | + | ||
65 | + @Override | ||
66 | + public TrafficSelector selector() { | ||
67 | + return selector; | ||
68 | + } | ||
69 | + | ||
70 | + @Override | ||
71 | + public Integer nextId() { | ||
72 | + return nextId; | ||
73 | + } | ||
74 | + | ||
75 | + @Override | ||
76 | + public TrafficTreatment treatment() { | ||
77 | + return treatment; | ||
78 | + } | ||
79 | + | ||
80 | + | ||
81 | + @Override | ||
82 | + public Flag flag() { | ||
83 | + return flag; | ||
84 | + } | ||
85 | + | ||
86 | + @Override | ||
87 | + public int id() { | ||
88 | + return id; | ||
89 | + } | ||
90 | + | ||
91 | + @Override | ||
92 | + public int priority() { | ||
93 | + return priority; | ||
94 | + } | ||
95 | + | ||
96 | + @Override | ||
97 | + public ApplicationId appId() { | ||
98 | + return appId; | ||
99 | + } | ||
100 | + | ||
101 | + @Override | ||
102 | + public int timeout() { | ||
103 | + return timeout; | ||
104 | + } | ||
105 | + | ||
106 | + @Override | ||
107 | + public boolean permanent() { | ||
108 | + return permanent; | ||
109 | + } | ||
110 | + | ||
111 | + @Override | ||
112 | + public Operation op() { | ||
113 | + return op; | ||
114 | + } | ||
115 | + | ||
116 | + /** | ||
117 | + * Returns a new builder. | ||
118 | + * | ||
119 | + * @return new builder | ||
120 | + */ | ||
121 | + public static Builder builder() { | ||
122 | + return new Builder(); | ||
123 | + } | ||
124 | + | ||
125 | + public static final class Builder implements ForwardingObjective.Builder { | ||
126 | + | ||
127 | + private TrafficSelector selector; | ||
128 | + private Flag flag; | ||
129 | + private boolean permanent = DEFAULT_PERMANENT; | ||
130 | + private int timeout = DEFAULT_TIMEOUT; | ||
131 | + private int priority = DEFAULT_PRIORITY; | ||
132 | + private ApplicationId appId; | ||
133 | + private Integer nextId; | ||
134 | + private TrafficTreatment treatment; | ||
135 | + | ||
136 | + @Override | ||
137 | + public Builder withSelector(TrafficSelector selector) { | ||
138 | + this.selector = selector; | ||
139 | + return this; | ||
140 | + } | ||
141 | + | ||
142 | + @Override | ||
143 | + public Builder nextStep(int nextId) { | ||
144 | + this.nextId = nextId; | ||
145 | + return this; | ||
146 | + } | ||
147 | + | ||
148 | + @Override | ||
149 | + public Builder withTreatment(TrafficTreatment treatment) { | ||
150 | + this.treatment = treatment; | ||
151 | + return this; | ||
152 | + } | ||
153 | + | ||
154 | + @Override | ||
155 | + public Builder withFlag(Flag flag) { | ||
156 | + this.flag = flag; | ||
157 | + return this; | ||
158 | + } | ||
159 | + | ||
160 | + @Override | ||
161 | + public Builder makeTemporary(int timeout) { | ||
162 | + this.timeout = timeout; | ||
163 | + this.permanent = false; | ||
164 | + return this; | ||
165 | + } | ||
166 | + | ||
167 | + @Override | ||
168 | + public Builder makePermanent() { | ||
169 | + this.permanent = true; | ||
170 | + return this; | ||
171 | + } | ||
172 | + | ||
173 | + @Override | ||
174 | + public Builder fromApp(ApplicationId appId) { | ||
175 | + this.appId = appId; | ||
176 | + return this; | ||
177 | + } | ||
178 | + | ||
179 | + @Override | ||
180 | + public Builder withPriority(int priority) { | ||
181 | + this.priority = priority; | ||
182 | + return this; | ||
183 | + } | ||
184 | + | ||
185 | + @Override | ||
186 | + public ForwardingObjective add() { | ||
187 | + checkNotNull(selector, "Must have a selector"); | ||
188 | + checkNotNull(flag, "A flag must be set"); | ||
189 | + checkArgument(nextId != null && treatment != null, "Must supply at " + | ||
190 | + "least a treatment and/or a nextId"); | ||
191 | + checkNotNull(appId, "Must supply an application id"); | ||
192 | + return new DefaultForwardingObjective(selector, flag, permanent, | ||
193 | + timeout, appId, priority, | ||
194 | + nextId, treatment, Operation.ADD); | ||
195 | + } | ||
196 | + | ||
197 | + @Override | ||
198 | + public ForwardingObjective remove() { | ||
199 | + checkNotNull(selector, "Must have a selector"); | ||
200 | + checkNotNull(flag, "A flag must be set"); | ||
201 | + checkArgument(nextId != null && treatment != null, "Must supply at " + | ||
202 | + "least a treatment and/or a nextId"); | ||
203 | + checkNotNull(appId, "Must supply an application id"); | ||
204 | + return new DefaultForwardingObjective(selector, flag, permanent, | ||
205 | + timeout, appId, priority, | ||
206 | + nextId, treatment, Operation.REMOVE); | ||
207 | + } | ||
208 | + } | ||
209 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.net.flowobjective; | ||
17 | + | ||
18 | +import com.google.common.collect.ImmutableList; | ||
19 | +import org.onosproject.core.ApplicationId; | ||
20 | +import org.onosproject.net.flow.TrafficTreatment; | ||
21 | + | ||
22 | +import java.util.Collection; | ||
23 | +import java.util.List; | ||
24 | + | ||
25 | +import static com.google.common.base.Preconditions.checkArgument; | ||
26 | +import static com.google.common.base.Preconditions.checkNotNull; | ||
27 | + | ||
28 | +/** | ||
29 | + * Default implementation of a next objective. | ||
30 | + */ | ||
31 | +public final class DefaultNextObjective implements NextObjective { | ||
32 | + | ||
33 | + private final List<TrafficTreatment> treatments; | ||
34 | + private final ApplicationId appId; | ||
35 | + private final Type type; | ||
36 | + private final Integer id; | ||
37 | + | ||
38 | + private DefaultNextObjective(Integer id, List<TrafficTreatment> treatments, | ||
39 | + ApplicationId appId, Type type) { | ||
40 | + this.treatments = treatments; | ||
41 | + this.appId = appId; | ||
42 | + this.type = type; | ||
43 | + this.id = id; | ||
44 | + } | ||
45 | + | ||
46 | + @Override | ||
47 | + public Collection<TrafficTreatment> next() { | ||
48 | + return treatments; | ||
49 | + } | ||
50 | + | ||
51 | + @Override | ||
52 | + public Type type() { | ||
53 | + return type; | ||
54 | + } | ||
55 | + | ||
56 | + @Override | ||
57 | + public int id() { | ||
58 | + return id; | ||
59 | + } | ||
60 | + | ||
61 | + @Override | ||
62 | + public int priority() { | ||
63 | + return 0; | ||
64 | + } | ||
65 | + | ||
66 | + @Override | ||
67 | + public ApplicationId appId() { | ||
68 | + return appId; | ||
69 | + } | ||
70 | + | ||
71 | + @Override | ||
72 | + public int timeout() { | ||
73 | + return 0; | ||
74 | + } | ||
75 | + | ||
76 | + @Override | ||
77 | + public boolean permanent() { | ||
78 | + return false; | ||
79 | + } | ||
80 | + | ||
81 | + @Override | ||
82 | + public Operation op() { | ||
83 | + throw new UnsupportedOperationException("Next Objective has no operation"); | ||
84 | + } | ||
85 | + | ||
86 | + /** | ||
87 | + * Returns a new builder. | ||
88 | + * | ||
89 | + * @return new builder | ||
90 | + */ | ||
91 | + public static Builder builder() { | ||
92 | + return new Builder(); | ||
93 | + } | ||
94 | + | ||
95 | + public static final class Builder implements NextObjective.Builder { | ||
96 | + | ||
97 | + private ApplicationId appId; | ||
98 | + private Type type; | ||
99 | + private Integer id; | ||
100 | + | ||
101 | + private final ImmutableList.Builder<TrafficTreatment> listBuilder | ||
102 | + = ImmutableList.builder(); | ||
103 | + | ||
104 | + | ||
105 | + | ||
106 | + @Override | ||
107 | + public NextObjective.Builder withId(int nextId) { | ||
108 | + this.id = nextId; | ||
109 | + return this; | ||
110 | + } | ||
111 | + | ||
112 | + @Override | ||
113 | + public Builder withType(Type type) { | ||
114 | + this.type = type; | ||
115 | + return this; | ||
116 | + } | ||
117 | + | ||
118 | + @Override | ||
119 | + public Builder addTreatment(TrafficTreatment treatment) { | ||
120 | + listBuilder.add(treatment); | ||
121 | + return this; | ||
122 | + } | ||
123 | + | ||
124 | + /** | ||
125 | + * Noop. This method has no effect. | ||
126 | + * | ||
127 | + * @param timeout a timeout | ||
128 | + * @return a next objective builder | ||
129 | + */ | ||
130 | + @Override | ||
131 | + public Builder makeTemporary(int timeout) { | ||
132 | + return this; | ||
133 | + } | ||
134 | + | ||
135 | + /** | ||
136 | + * Noop. This method has no effect. | ||
137 | + * | ||
138 | + * @return a next objective builder | ||
139 | + */ | ||
140 | + @Override | ||
141 | + public Builder makePermanent() { | ||
142 | + return this; | ||
143 | + } | ||
144 | + | ||
145 | + @Override | ||
146 | + public Builder fromApp(ApplicationId appId) { | ||
147 | + this.appId = appId; | ||
148 | + return this; | ||
149 | + } | ||
150 | + | ||
151 | + /** | ||
152 | + * Noop. This method has no effect. | ||
153 | + * | ||
154 | + * @param priority an integer | ||
155 | + * @return a next objective builder | ||
156 | + */ | ||
157 | + @Override | ||
158 | + public Builder withPriority(int priority) { | ||
159 | + return this; | ||
160 | + } | ||
161 | + | ||
162 | + @Override | ||
163 | + public NextObjective build() { | ||
164 | + List<TrafficTreatment> treatments = listBuilder.build(); | ||
165 | + checkNotNull(appId, "Must supply an application id"); | ||
166 | + checkNotNull(id, "id cannot be null"); | ||
167 | + checkNotNull(type, "The type cannot be null"); | ||
168 | + checkArgument(!treatments.isEmpty(), "Must have at least one treatment"); | ||
169 | + | ||
170 | + return new DefaultNextObjective(id, treatments, appId, type); | ||
171 | + } | ||
172 | + } | ||
173 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.net.flowobjective; | ||
17 | + | ||
18 | +import org.onosproject.net.flow.criteria.Criterion; | ||
19 | + | ||
20 | +import java.util.Collection; | ||
21 | + | ||
22 | +/** | ||
23 | + * Represents a filtering flow objective. Each filter is mapping | ||
24 | + * from a criterion to a collection of criteria. The mapping will | ||
25 | + * be used by a device driver to construct the actual flow rules to | ||
26 | + * be installed on the device. | ||
27 | + */ | ||
28 | +public interface FilteringObjective extends Objective { | ||
29 | + | ||
30 | + /** | ||
31 | + * Represents filtering key used in this filter. | ||
32 | + * | ||
33 | + * @return a criterion | ||
34 | + */ | ||
35 | + Criterion key(); | ||
36 | + | ||
37 | + /** | ||
38 | + * The set of conditions the filter must provision at the device. | ||
39 | + * | ||
40 | + * @return a collection of criteria | ||
41 | + */ | ||
42 | + Collection<Criterion> conditions(); | ||
43 | + | ||
44 | + /** | ||
45 | + * Builder of Filtering objective entities. | ||
46 | + */ | ||
47 | + public interface Builder extends Objective.Builder { | ||
48 | + | ||
49 | + /** | ||
50 | + * Add a filtering condition. | ||
51 | + * | ||
52 | + * @param criterion new criterion | ||
53 | + * @return a filtering builder | ||
54 | + */ | ||
55 | + public Builder addCondition(Criterion criterion); | ||
56 | + | ||
57 | + /** | ||
58 | + * Add a filtering key. | ||
59 | + * | ||
60 | + * @param criterion new criterion | ||
61 | + * @return a filtering builder. | ||
62 | + */ | ||
63 | + public Builder withKey(Criterion criterion); | ||
64 | + | ||
65 | + /** | ||
66 | + * Builds the filtering objective that will be added. | ||
67 | + * | ||
68 | + * @return a filtering objective | ||
69 | + */ | ||
70 | + public FilteringObjective add(); | ||
71 | + | ||
72 | + /** | ||
73 | + * Builds the filtering objective that will be removed. | ||
74 | + * | ||
75 | + * @return a filtering objective. | ||
76 | + */ | ||
77 | + public FilteringObjective remove(); | ||
78 | + | ||
79 | + | ||
80 | + } | ||
81 | + | ||
82 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.net.flowobjective; | ||
17 | + | ||
18 | +import org.onosproject.net.flow.TrafficSelector; | ||
19 | +import org.onosproject.net.flow.TrafficTreatment; | ||
20 | + | ||
21 | +/** | ||
22 | + * Represents a description of which types of traffic need to | ||
23 | + * be forwarded through the device. A forwarding objective may | ||
24 | + * in multiple rules at the device. | ||
25 | + */ | ||
26 | +public interface ForwardingObjective extends Objective { | ||
27 | + | ||
28 | + /** | ||
29 | + * Represents whether this objective is monolithic or | ||
30 | + * may be broken down into parts. | ||
31 | + */ | ||
32 | + enum Flag { | ||
33 | + /** | ||
34 | + * A decomposable objective. | ||
35 | + */ | ||
36 | + SPECIFIC, | ||
37 | + | ||
38 | + /** | ||
39 | + * A monolithic objective. | ||
40 | + */ | ||
41 | + VERSATILE | ||
42 | + } | ||
43 | + | ||
44 | + /** | ||
45 | + * Obtain the selector for this objective. | ||
46 | + * | ||
47 | + * @return a traffic selector | ||
48 | + */ | ||
49 | + TrafficSelector selector(); | ||
50 | + | ||
51 | + /** | ||
52 | + * Obtain the traffic treatment for this objective. Mutually exclusive with | ||
53 | + * 'treatment'. | ||
54 | + * | ||
55 | + * @return an integer | ||
56 | + */ | ||
57 | + Integer nextId(); | ||
58 | + | ||
59 | + /** | ||
60 | + * A traffic treatment for this forwarding objective. Mutually exclusive | ||
61 | + * with a nextId. | ||
62 | + * | ||
63 | + * @return a traffic treatment | ||
64 | + */ | ||
65 | + TrafficTreatment treatment(); | ||
66 | + | ||
67 | + /** | ||
68 | + * Obtain the type of this objective. | ||
69 | + * | ||
70 | + * @return a flag type | ||
71 | + */ | ||
72 | + Flag flag(); | ||
73 | + | ||
74 | + /** | ||
75 | + * A forwarding objective builder. | ||
76 | + */ | ||
77 | + public interface Builder extends Objective.Builder { | ||
78 | + | ||
79 | + /** | ||
80 | + * Assigns a selector to the forwarding objective. | ||
81 | + * | ||
82 | + * @param selector a traffic selector | ||
83 | + * @return a forwarding objective builder | ||
84 | + */ | ||
85 | + public Builder withSelector(TrafficSelector selector); | ||
86 | + | ||
87 | + /** | ||
88 | + * Assigns a next step to the forwarding objective. | ||
89 | + * | ||
90 | + * @param nextId a next objective id. | ||
91 | + * @return a forwarding objective builder | ||
92 | + */ | ||
93 | + public Builder nextStep(int nextId); | ||
94 | + | ||
95 | + /** | ||
96 | + * Assigns the treatment for this forwarding objective. | ||
97 | + * | ||
98 | + * @param treatment a traffic treatment | ||
99 | + * @return a forwarding objective | ||
100 | + */ | ||
101 | + public Builder withTreatment(TrafficTreatment treatment); | ||
102 | + | ||
103 | + /** | ||
104 | + * Assigns the flag to the forwarding objective. | ||
105 | + * | ||
106 | + * @param flag a flag | ||
107 | + * @return a forwarding objective builder | ||
108 | + */ | ||
109 | + public Builder withFlag(Flag flag); | ||
110 | + | ||
111 | + /** | ||
112 | + * Builds the forwarding objective that will be added. | ||
113 | + * | ||
114 | + * @return a forwarding objective | ||
115 | + */ | ||
116 | + public ForwardingObjective add(); | ||
117 | + | ||
118 | + /** | ||
119 | + * Builds the forwarding objective that will be removed. | ||
120 | + * | ||
121 | + * @return a forwarding objective. | ||
122 | + */ | ||
123 | + public ForwardingObjective remove(); | ||
124 | + } | ||
125 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.net.flowobjective; | ||
17 | + | ||
18 | +import org.onosproject.net.flow.TrafficTreatment; | ||
19 | + | ||
20 | +import java.util.Collection; | ||
21 | + | ||
22 | +/** | ||
23 | + * Represents a nexthop which will be translated by a driver | ||
24 | + * into the appropriate group or actions needed to implement | ||
25 | + * the function. | ||
26 | + */ | ||
27 | +public interface NextObjective extends Objective { | ||
28 | + | ||
29 | + /** | ||
30 | + * Represents the type of next phase to build. | ||
31 | + */ | ||
32 | + enum Type { | ||
33 | + /** | ||
34 | + * A hashed packet processing. | ||
35 | + */ | ||
36 | + HASHED, | ||
37 | + | ||
38 | + /** | ||
39 | + * Broadcast packet process. | ||
40 | + */ | ||
41 | + BROADCAST, | ||
42 | + | ||
43 | + /** | ||
44 | + * Failover handling. | ||
45 | + */ | ||
46 | + FAILOVER, | ||
47 | + | ||
48 | + /** | ||
49 | + * Simple processing. Could be a group or a treatment. | ||
50 | + */ | ||
51 | + SIMPLE | ||
52 | + } | ||
53 | + | ||
54 | + /** | ||
55 | + * The collection of treatments that need to be applied to a set of traffic. | ||
56 | + * | ||
57 | + * @return a collection of traffic treatments | ||
58 | + */ | ||
59 | + Collection<TrafficTreatment> next(); | ||
60 | + | ||
61 | + /** | ||
62 | + * The type of operation that will be applied to the traffic using the collection | ||
63 | + * of treatments. | ||
64 | + * | ||
65 | + * @return a type | ||
66 | + */ | ||
67 | + Type type(); | ||
68 | + | ||
69 | + /** | ||
70 | + * A next step builder. | ||
71 | + */ | ||
72 | + public interface Builder extends Objective.Builder { | ||
73 | + | ||
74 | + /** | ||
75 | + * Specifies the id for this next objective. | ||
76 | + * | ||
77 | + * @param nextId an integer | ||
78 | + * @return a next objective builder | ||
79 | + */ | ||
80 | + public Builder withId(int nextId); | ||
81 | + | ||
82 | + /** | ||
83 | + * Sets the type of next step. | ||
84 | + * | ||
85 | + * @param type a type | ||
86 | + * @return a next step builder | ||
87 | + */ | ||
88 | + public Builder withType(Type type); | ||
89 | + | ||
90 | + /** | ||
91 | + * Adds a treatment to this next step. | ||
92 | + * | ||
93 | + * @param treatment a traffic treatment | ||
94 | + * @return a next step builder | ||
95 | + */ | ||
96 | + public Builder addTreatment(TrafficTreatment treatment); | ||
97 | + | ||
98 | + /** | ||
99 | + * Builds a next step. | ||
100 | + * | ||
101 | + * @return a next step | ||
102 | + */ | ||
103 | + public NextObjective build(); | ||
104 | + | ||
105 | + } | ||
106 | + | ||
107 | +} |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.net.flowobjective; | ||
17 | + | ||
18 | +import org.onosproject.core.ApplicationId; | ||
19 | + | ||
20 | +/** | ||
21 | + * Base representation of an flow description. | ||
22 | + */ | ||
23 | +public interface Objective { | ||
24 | + | ||
25 | + static final boolean DEFAULT_PERMANENT = true; | ||
26 | + static final int DEFAULT_TIMEOUT = 0; | ||
27 | + static final int DEFAULT_PRIORITY = 32768; | ||
28 | + | ||
29 | + /** | ||
30 | + * Type of operation. | ||
31 | + */ | ||
32 | + enum Operation { | ||
33 | + /** | ||
34 | + * Adds the objective. | ||
35 | + */ | ||
36 | + ADD, | ||
37 | + | ||
38 | + /** | ||
39 | + * Removes the objective. | ||
40 | + */ | ||
41 | + REMOVE | ||
42 | + } | ||
43 | + | ||
44 | + /** | ||
45 | + * An identifier for this objective. | ||
46 | + * | ||
47 | + * @return an integer | ||
48 | + */ | ||
49 | + int id(); | ||
50 | + | ||
51 | + /** | ||
52 | + * The priority for this objective. | ||
53 | + * | ||
54 | + * @return an integer | ||
55 | + */ | ||
56 | + int priority(); | ||
57 | + | ||
58 | + /** | ||
59 | + * The application which applied this objective. | ||
60 | + * | ||
61 | + * @return an application id | ||
62 | + */ | ||
63 | + ApplicationId appId(); | ||
64 | + | ||
65 | + /** | ||
66 | + * The timeout for this objective. | ||
67 | + * | ||
68 | + * @return an integer | ||
69 | + */ | ||
70 | + int timeout(); | ||
71 | + | ||
72 | + /** | ||
73 | + * Whether this objective is permanent. | ||
74 | + * | ||
75 | + * @return a boolean | ||
76 | + */ | ||
77 | + boolean permanent(); | ||
78 | + | ||
79 | + /** | ||
80 | + * The type of operation for this objective. | ||
81 | + * | ||
82 | + * @return an operation | ||
83 | + */ | ||
84 | + Operation op(); | ||
85 | + | ||
86 | + /** | ||
87 | + * An objective builder. | ||
88 | + */ | ||
89 | + public interface Builder { | ||
90 | + /** | ||
91 | + * Makes the filtering objective temporary. | ||
92 | + * | ||
93 | + * @param timeout a timeout | ||
94 | + * @return an objective builder | ||
95 | + */ | ||
96 | + public Builder makeTemporary(int timeout); | ||
97 | + | ||
98 | + /** | ||
99 | + * Makes the filtering objective permanent. | ||
100 | + * | ||
101 | + * @return an objective builder | ||
102 | + */ | ||
103 | + public Builder makePermanent(); | ||
104 | + | ||
105 | + /** | ||
106 | + * Specifies the application which applied the filter. | ||
107 | + * | ||
108 | + * @param appId an application id | ||
109 | + * @return an objective builder | ||
110 | + */ | ||
111 | + public Builder fromApp(ApplicationId appId); | ||
112 | + | ||
113 | + /** | ||
114 | + * Sets the priority for this objective. | ||
115 | + * | ||
116 | + * @param priority an integer | ||
117 | + * @return an objective builder | ||
118 | + */ | ||
119 | + public Builder withPriority(int priority); | ||
120 | + } | ||
121 | + | ||
122 | +} |
drivers/pom.xml
0 → 100644
1 | +<?xml version="1.0" encoding="UTF-8"?> | ||
2 | +<!-- | ||
3 | + ~ Copyright 2014 Open Networking Laboratory | ||
4 | + ~ | ||
5 | + ~ Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | + ~ you may not use this file except in compliance with the License. | ||
7 | + ~ You may obtain a copy of the License at | ||
8 | + ~ | ||
9 | + ~ http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | + ~ | ||
11 | + ~ Unless required by applicable law or agreed to in writing, software | ||
12 | + ~ distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | + ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | + ~ See the License for the specific language governing permissions and | ||
15 | + ~ limitations under the License. | ||
16 | + --> | ||
17 | +<project xmlns="http://maven.apache.org/POM/4.0.0" | ||
18 | + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
19 | + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd"> | ||
20 | + <modelVersion>4.0.0</modelVersion> | ||
21 | + | ||
22 | + | ||
23 | + <parent> | ||
24 | + <groupId>org.onosproject</groupId> | ||
25 | + <artifactId>onos</artifactId> | ||
26 | + <version>1.2.0-SNAPSHOT</version> | ||
27 | + <relativePath>../pom.xml</relativePath> | ||
28 | + </parent> | ||
29 | + | ||
30 | + <artifactId>onos-drivers</artifactId> | ||
31 | + <packaging>bundle</packaging> | ||
32 | + | ||
33 | + <description>ONOS Default Device Drivers</description> | ||
34 | + | ||
35 | + <dependencies> | ||
36 | + <dependency> | ||
37 | + <groupId>org.osgi</groupId> | ||
38 | + <artifactId>org.osgi.compendium</artifactId> | ||
39 | + </dependency> | ||
40 | + | ||
41 | + <dependency> | ||
42 | + <groupId>org.onosproject</groupId> | ||
43 | + <artifactId>onos-api</artifactId> | ||
44 | + </dependency> | ||
45 | + | ||
46 | + <dependency> | ||
47 | + <groupId>org.easymock</groupId> | ||
48 | + <artifactId>easymock</artifactId> | ||
49 | + <scope>test</scope> | ||
50 | + </dependency> | ||
51 | + | ||
52 | + </dependencies> | ||
53 | + | ||
54 | + <build> | ||
55 | + <plugins> | ||
56 | + <plugin> | ||
57 | + <groupId>org.apache.felix</groupId> | ||
58 | + <artifactId>maven-bundle-plugin</artifactId> | ||
59 | + </plugin> | ||
60 | + | ||
61 | + </plugins> | ||
62 | + </build> | ||
63 | +</project> |
1 | +/* | ||
2 | + * Copyright 2015 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 | +package org.onosproject.driver.pipeline; | ||
17 | + | ||
18 | +import com.google.common.util.concurrent.SettableFuture; | ||
19 | +import org.onlab.osgi.ServiceDirectory; | ||
20 | +import org.onosproject.core.DefaultGroupId; | ||
21 | +import org.onosproject.net.DeviceId; | ||
22 | +import org.onosproject.net.behaviour.Pipeliner; | ||
23 | +import org.onosproject.net.flow.DefaultFlowRule; | ||
24 | +import org.onosproject.net.flow.FlowRule; | ||
25 | +import org.onosproject.net.flow.FlowRuleOperations; | ||
26 | +import org.onosproject.net.flow.FlowRuleOperationsContext; | ||
27 | +import org.onosproject.net.flow.FlowRuleService; | ||
28 | +import org.onosproject.net.flow.TrafficSelector; | ||
29 | +import org.onosproject.net.flowobjective.FilteringObjective; | ||
30 | +import org.onosproject.net.flowobjective.ForwardingObjective; | ||
31 | +import org.slf4j.Logger; | ||
32 | + | ||
33 | +import java.util.Collection; | ||
34 | +import java.util.concurrent.Future; | ||
35 | + | ||
36 | +import static org.slf4j.LoggerFactory.getLogger; | ||
37 | + | ||
38 | +/** | ||
39 | + * Simple single table pipeline abstraction. | ||
40 | + */ | ||
41 | +public class DefaultSingleTablePipeline implements Pipeliner { | ||
42 | + | ||
43 | + private final Logger log = getLogger(getClass()); | ||
44 | + | ||
45 | + private ServiceDirectory serviceDirectory; | ||
46 | + private FlowRuleService flowRuleService; | ||
47 | + private DeviceId deviceId; | ||
48 | + | ||
49 | + @Override | ||
50 | + public void init(DeviceId deviceId, ServiceDirectory serviceDirectory) { | ||
51 | + this.serviceDirectory = serviceDirectory; | ||
52 | + this.deviceId = deviceId; | ||
53 | + | ||
54 | + flowRuleService = serviceDirectory.get(FlowRuleService.class); | ||
55 | + | ||
56 | + } | ||
57 | + | ||
58 | + @Override | ||
59 | + public Future<Boolean> filter(Collection<FilteringObjective> filters) { | ||
60 | + throw new UnsupportedOperationException("Single table does not filter."); | ||
61 | + } | ||
62 | + | ||
63 | + @Override | ||
64 | + public Future<Boolean> forward(Collection<ForwardingObjective> forwardings) { | ||
65 | + FlowRuleOperations.Builder flowBuilder = FlowRuleOperations.builder(); | ||
66 | + forwardings.forEach(fwd -> { | ||
67 | + if (fwd.flag() != ForwardingObjective.Flag.VERSATILE) { | ||
68 | + throw new UnsupportedOperationException( | ||
69 | + "Only VERSATILE is supported."); | ||
70 | + } | ||
71 | + | ||
72 | + TrafficSelector selector = fwd.selector(); | ||
73 | + | ||
74 | + FlowRule rule = new DefaultFlowRule(deviceId, selector, | ||
75 | + fwd.treatment(), | ||
76 | + fwd.priority(), fwd.appId(), | ||
77 | + new DefaultGroupId(fwd.id()), | ||
78 | + fwd.timeout(), fwd.permanent()); | ||
79 | + | ||
80 | + switch (fwd.op()) { | ||
81 | + | ||
82 | + case ADD: | ||
83 | + flowBuilder.add(rule); | ||
84 | + break; | ||
85 | + case REMOVE: | ||
86 | + flowBuilder.remove(rule); | ||
87 | + break; | ||
88 | + default: | ||
89 | + log.warn("Unknown operation {}", fwd.op()); | ||
90 | + } | ||
91 | + | ||
92 | + }); | ||
93 | + | ||
94 | + SettableFuture<Boolean> future = SettableFuture.create(); | ||
95 | + | ||
96 | + flowRuleService.apply(flowBuilder.build(new FlowRuleOperationsContext() { | ||
97 | + @Override | ||
98 | + public void onSuccess(FlowRuleOperations ops) { | ||
99 | + future.set(true); | ||
100 | + } | ||
101 | + | ||
102 | + @Override | ||
103 | + public void onError(FlowRuleOperations ops) { | ||
104 | + future.set(false); | ||
105 | + } | ||
106 | + })); | ||
107 | + return future; | ||
108 | + } | ||
109 | +} |
... | @@ -44,6 +44,7 @@ | ... | @@ -44,6 +44,7 @@ |
44 | <module>web</module> | 44 | <module>web</module> |
45 | <module>cli</module> | 45 | <module>cli</module> |
46 | <module>providers</module> | 46 | <module>providers</module> |
47 | + <module>drivers</module> | ||
47 | <module>openflow</module> | 48 | <module>openflow</module> |
48 | <module>apps</module> | 49 | <module>apps</module> |
49 | <module>features</module> | 50 | <module>features</module> | ... | ... |
-
Please register or login to post a comment