blob: a5c5557fa0e8877b7c67fa5621af5091045e4dfc [file] [log] [blame]
Simon Hunt0c3a7bb2017-10-05 17:00:56 -07001/*
2 * Copyright 2017-present Open Networking Foundation
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.segmentrouting.config;
18
19import com.fasterxml.jackson.databind.JsonNode;
20import com.fasterxml.jackson.databind.node.ArrayNode;
21import org.onosproject.core.ApplicationId;
22import org.onosproject.net.config.Config;
23
24import java.util.ArrayList;
25import java.util.Iterator;
26import java.util.List;
27import java.util.NoSuchElementException;
28import java.util.regex.Matcher;
29import java.util.regex.Pattern;
30
31/**
32 * Network config to describe ports that should be blocked until authenticated.
33 */
34public class BlockedPortsConfig extends Config<ApplicationId> {
35
36 /**
37 * Returns the top level keys to the config,
38 * which should be device ID strings.
39 *
40 * @return this list of top level keys
41 */
42 public List<String> deviceIds() {
43 List<String> devIds = new ArrayList<>();
44 if (object != null) {
45 Iterator<String> it = object.fieldNames();
46 if (it != null) {
47 it.forEachRemaining(devIds::add);
48 }
49 }
50 return devIds;
51 }
52
53 /**
54 * Returns the port range strings associated with the given device id key.
55 *
56 * @param deviceId the device id key
57 * @return the associated port range strings
58 */
59 public List<String> portRanges(String deviceId) {
60 List<String> portRanges = new ArrayList<>();
61 if (object != null) {
62 JsonNode jnode = object.get(deviceId);
63 if (ArrayNode.class.isInstance(jnode)) {
64 ArrayNode array = (ArrayNode) jnode;
65 array.forEach(pr -> portRanges.add(pr.asText()));
66 }
67 }
68 return portRanges;
69 }
70
71 /**
72 * Returns an iterator over the port numbers defined by the port ranges
73 * defined in the configuration, for the given device.
74 *
75 * @param deviceId the specific device
76 * @return an iterator over the configured ports
77 */
78 public Iterator<Long> portIterator(String deviceId) {
79 List<String> ranges = portRanges(deviceId);
80 return new PortIterator(ranges);
81 }
82
83 /**
84 * Private implementation of an iterator that aggregates several range
85 * iterators into a single iterator.
86 */
87 class PortIterator implements Iterator<Long> {
88 private final List<Range> ranges;
89 private final int nRanges;
90 private int currentRange = 0;
91 private Iterator<Long> iterator;
92
93 PortIterator(List<String> rangeSpecs) {
94 nRanges = rangeSpecs.size();
95 ranges = new ArrayList<>(nRanges);
96 if (nRanges > 0) {
97 for (String rs : rangeSpecs) {
98 ranges.add(new Range(rs));
99 }
100 iterator = ranges.get(0).iterator();
101 }
102 }
103
104 @Override
105 public boolean hasNext() {
106 return nRanges > 0 &&
107 (currentRange < nRanges - 1 ||
108 (currentRange < nRanges && iterator.hasNext()));
109 }
110
111 @Override
112 public Long next() {
113 if (nRanges == 0) {
114 throw new NoSuchElementException();
115 }
116
117 Long value;
118 if (iterator.hasNext()) {
119 value = iterator.next();
120 } else {
121 currentRange++;
122 if (currentRange < nRanges) {
123 iterator = ranges.get(currentRange).iterator();
124 value = iterator.next();
125 } else {
126 throw new NoSuchElementException();
127 }
128 }
129 return value;
130 }
131 }
132
133 /**
134 * Private implementation of a "range" of long numbers, defined by a
135 * string of the form {@code "<lo>-<hi>"}, for example, "17-32".
136 */
137 static final class Range {
138 private static final Pattern RE_SINGLE = Pattern.compile("(\\d+)");
139 private static final Pattern RE_RANGE = Pattern.compile("(\\d+)-(\\d+)");
140 private static final String E_BAD_FORMAT = "Bad Range Format ";
141
142 private final long lo;
143 private final long hi;
144
145 /**
146 * Constructs a range from the given string definition.
147 * For example:
148 * <pre>
149 * Range r = new Range("17-32");
150 * </pre>
151 *
152 * @param s the string representation of the range
153 * @throws IllegalArgumentException if the range string is malformed
154 */
155 Range(String s) {
156 String lohi = s;
157 Matcher m = RE_SINGLE.matcher(s);
158 if (m.matches()) {
159 lohi = s + "-" + s;
160 }
161 m = RE_RANGE.matcher(lohi);
162 if (!m.matches()) {
163 throw new IllegalArgumentException(E_BAD_FORMAT + s);
164 }
165 try {
166 lo = Long.parseLong(m.group(1));
167 hi = Long.parseLong(m.group(2));
168
169 if (hi < lo) {
170 throw new IllegalArgumentException(E_BAD_FORMAT + s);
171 }
172 } catch (NumberFormatException nfe) {
173 // unlikely to be thrown, since the matcher will have failed first
174 throw new IllegalArgumentException(E_BAD_FORMAT + s, nfe);
175 }
176 }
177
178
179 /**
180 * Returns an iterator over this range, starting from the lowest value
181 * and iterating up to the highest value (inclusive).
182 *
183 * @return an iterator over this range
184 */
185 Iterator<Long> iterator() {
186 return new RangeIterator();
187 }
188
189 /**
190 * Private implementation of an iterator over the range.
191 */
192 class RangeIterator implements Iterator<Long> {
193 long current;
194
195 RangeIterator() {
196 current = lo - 1;
197 }
198
199 @Override
200 public boolean hasNext() {
201 return current < hi;
202 }
203
204 @Override
205 public Long next() {
206 if (!hasNext()) {
207 throw new NoSuchElementException();
208 }
209 return ++current;
210 }
211 }
212 }
213}