blob: 894e2d2ccf385a8341e9027e1b4576717d6b36c0 [file] [log] [blame]
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -08001package net.onrc.onos.datastore.topology;
2
3import java.nio.ByteBuffer;
4import java.util.Arrays;
5import java.util.Collection;
6import java.util.Collections;
7import java.util.HashMap;
Yuta HIGUCHI10eebea2014-02-03 10:41:41 -08008import java.util.Iterator;
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -08009import java.util.Map;
10import java.util.Set;
11import java.util.TreeSet;
12
13import org.slf4j.Logger;
14import org.slf4j.LoggerFactory;
15
16import com.esotericsoftware.kryo.Kryo;
17
Yuta HIGUCHI10eebea2014-02-03 10:41:41 -080018import edu.stanford.ramcloud.JRamCloud;
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -080019import net.onrc.onos.datastore.RCObject;
20import net.onrc.onos.datastore.RCTable;
21import net.onrc.onos.datastore.utils.ByteArrayComparator;
Yuta HIGUCHIc9d06ef2014-01-31 15:55:12 -080022import net.onrc.onos.datastore.utils.ByteArrayUtil;
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -080023
24public class RCPort extends RCObject {
25 private static final Logger log = LoggerFactory.getLogger(RCPort.class);
26
27 private static final ThreadLocal<Kryo> portKryo = new ThreadLocal<Kryo>() {
28 @Override
29 protected Kryo initialValue() {
30 Kryo kryo = new Kryo();
31 kryo.setRegistrationRequired(true);
32 kryo.setReferences(false);
33 kryo.register(byte[].class);
34 kryo.register(byte[][].class);
35 kryo.register(HashMap.class);
36 // TODO check if we should explicitly specify EnumSerializer
37 kryo.register(STATUS.class);
38 return kryo;
39 }
40 };
41
42 public static final String GLOBAL_PORT_TABLE_NAME = "G:Port";
43
44 // FIXME these should be Enum or some number, not String
45 private static final String PROP_DPID = "dpid";
46 private static final String PROP_NUMBER = "number";
47 private static final String PROP_STATUS = "status";
48 private static final String PROP_LINK_IDS = "link-ids";
49 private static final String PROP_DEVICE_IDS = "device-ids";
50
51 // must not re-order enum members, ordinal will be sent over wire
52 public enum STATUS {
53 INACTIVE, ACTIVE;
54 }
55
56 private final Long dpid;
57 private final Long number;
58
59 private STATUS status;
60 // XXX These 2 set of Ids can be removed from DataStore, if In-Memory cache
61 // build the indexing info from Link.
62 private TreeSet<byte[]> linkIds;
63 transient boolean isLinkIdsModified;
64 private TreeSet<byte[]> deviceIds;
65 transient boolean isDeviceIdsModified;
66
67 public static final int PORTID_BYTES = RCSwitch.SWITCHID_BYTES + 2 + 8;
68
69 public static byte[] getPortID(Long dpid, Long number) {
70 if (dpid == null) {
71 throw new IllegalArgumentException("dpid cannot be null");
72 }
73 if (number == null) {
74 throw new IllegalArgumentException("number cannot be null");
75 }
76 return ByteBuffer.allocate(PORTID_BYTES).putChar('S').putLong(dpid)
77 .putChar('P').putLong(number).array();
78 }
79
Yuta HIGUCHIc9d06ef2014-01-31 15:55:12 -080080 public static long[] getPortPairFromKey(byte[] key) {
81 return getPortPairFromKey(ByteBuffer.wrap(key));
82
83 }
84
85 public static long[] getPortPairFromKey(ByteBuffer keyBuf) {
86 long[] pair = new long[2];
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -080087 if (keyBuf.getChar() != 'S') {
Yuta HIGUCHIc9d06ef2014-01-31 15:55:12 -080088 throw new IllegalArgumentException("Invalid Port key:" + keyBuf
89 + " "
90 + ByteArrayUtil.toHexStringBuffer(keyBuf.array(), ":"));
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -080091 }
Yuta HIGUCHIc9d06ef2014-01-31 15:55:12 -080092 pair[0] = keyBuf.getLong();
93 if (keyBuf.getChar() != 'P') {
94 throw new IllegalArgumentException("Invalid Port key:" + keyBuf
95 + " "
96 + ByteArrayUtil.toHexStringBuffer(keyBuf.array(), ":"));
97 }
98 pair[1] = keyBuf.getLong();
99 return pair;
100
101 }
102
103 public static long getDpidFromKey(byte[] key) {
104 return getPortPairFromKey(key)[0];
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -0800105 }
106
107 public static long getNumberFromKey(byte[] key) {
Yuta HIGUCHIc9d06ef2014-01-31 15:55:12 -0800108 return getPortPairFromKey(key)[1];
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -0800109 }
110
111 // FIXME specify DPID,number here, or Should caller specify the key it self?
112 // In other words, should layer above have the control of the ID?
113 public RCPort(Long dpid, Long number) {
114 super(RCTable.getTable(GLOBAL_PORT_TABLE_NAME), getPortID(dpid, number));
115
116 // TODO Auto-generated constructor stub
117
118 this.dpid = dpid;
119 this.number = number;
120 this.status = STATUS.INACTIVE;
121 this.linkIds = new TreeSet<>(ByteArrayComparator.BYTEARRAY_COMPARATOR);
122 this.isLinkIdsModified = true;
123 this.deviceIds = new TreeSet<>(ByteArrayComparator.BYTEARRAY_COMPARATOR);
124 this.isDeviceIdsModified = true;
125 }
126
Yuta HIGUCHI10eebea2014-02-03 10:41:41 -0800127 /**
128 * Get an instance from Key.
129 *
130 * @note You need to call `read()` to get the DB content.
131 * @param key
132 * @return RCPort instance
133 */
134 public static <P extends RCObject> P createFromKey(byte[] key) {
Yuta HIGUCHIc9d06ef2014-01-31 15:55:12 -0800135 long[] pair = getPortPairFromKey(key);
Yuta HIGUCHI10eebea2014-02-03 10:41:41 -0800136 @SuppressWarnings("unchecked")
137 P p = (P) new RCPort(pair[0], pair[1]);
138 return p;
139 }
140
141 public static Iterable<RCPort> getAllPorts() {
142 return new PortEnumerator();
143 }
144
145 public static class PortEnumerator implements Iterable<RCPort> {
146
147 @Override
148 public Iterator<RCPort> iterator() {
149 return new PortIterator();
150 }
151 }
152
153 public static class PortIterator extends ObjectIterator<RCPort> {
154
155 public PortIterator() {
156 super(RCTable.getTable(GLOBAL_PORT_TABLE_NAME));
157 }
158
159 @Override
160 public RCPort next() {
161 JRamCloud.Object o = enumerator.next();
162 RCPort e = RCPort.createFromKey(o.key);
163 e.setValueAndDeserialize(o.value, o.version);
164 return e;
165 }
Yuta HIGUCHI1ef85c42014-01-29 17:23:21 -0800166 }
167
168 public STATUS getStatus() {
169 return status;
170 }
171
172 public void setStatus(STATUS status) {
173 this.status = status;
174 getObjectMap().put(PROP_STATUS, status);
175 }
176
177 public Long getDpid() {
178 return dpid;
179 }
180
181 public Long getNumber() {
182 return number;
183 }
184
185 public byte[] getId() {
186 return getKey();
187 }
188
189 public void addLinkId(byte[] linkId) {
190 isLinkIdsModified |= linkIds.add(linkId);
191 }
192
193 public void removeLinkId(byte[] linkId) {
194 isLinkIdsModified |= linkIds.remove(linkId);
195 }
196
197 public void emptyLinkIds() {
198 linkIds.clear();
199 isLinkIdsModified = true;
200 }
201
202 public void addAllToLinkIds(Collection<byte[]> linkIds) {
203 isLinkIdsModified |= this.linkIds.addAll(linkIds);
204 }
205
206 /**
207 *
208 * @return Unmodifiable Set view of all the LinkIds;
209 */
210 public Set<byte[]> getAllLinkIds() {
211 return Collections.unmodifiableSet(linkIds);
212 }
213
214 public void addDeviceId(byte[] deviceId) {
215 isDeviceIdsModified |= deviceIds.add(deviceId);
216 }
217
218 public void removeDeviceId(byte[] deviceId) {
219 isDeviceIdsModified |= deviceIds.remove(deviceId);
220 }
221
222 public void emptyDeviceIds() {
223 deviceIds.clear();
224 isDeviceIdsModified = true;
225 }
226
227 public void addAllToDeviceIds(Collection<byte[]> deviceIds) {
228 isDeviceIdsModified |= this.deviceIds.addAll(deviceIds);
229 }
230
231 /**
232 *
233 * @return Unmodifiable Set view of all the LinkIds;
234 */
235 public Set<byte[]> getAllDeviceIds() {
236 return Collections.unmodifiableSet(deviceIds);
237 }
238
239 @Override
240 public void serializeAndSetValue() {
241 Map<Object, Object> map = getObjectMap();
242
243 map.put(PROP_DPID, this.dpid);
244 map.put(PROP_NUMBER, this.number);
245 if (isLinkIdsModified) {
246 byte[] linkIdArray[] = new byte[linkIds.size()][];
247 map.put(PROP_LINK_IDS, linkIds.toArray(linkIdArray));
248 isLinkIdsModified = false;
249 }
250 if (isDeviceIdsModified) {
251 byte[] deviceIdArray[] = new byte[deviceIds.size()][];
252 map.put(PROP_DEVICE_IDS, deviceIds.toArray(deviceIdArray));
253 isDeviceIdsModified = false;
254 }
255 if (log.isWarnEnabled() && (linkIds.size() * deviceIds.size()) != 0) {
256 log.warn("Either #LinkIds:{} or #DeviceIds:{} is expected to be 0",
257 linkIds.size(), deviceIds.size());
258 }
259
260 serializeAndSetValue(portKryo.get(), map);
261 }
262
263 @Override
264 public Map<Object, Object> deserializeObjectFromValue() {
265 Map<Object, Object> map = deserializeObjectFromValue(portKryo.get());
266
267 this.status = (STATUS) map.get(PROP_STATUS);
268
269 if (this.linkIds == null) {
270 this.linkIds = new TreeSet<>(
271 ByteArrayComparator.BYTEARRAY_COMPARATOR);
272 }
273 byte[] linkIdArray[] = (byte[][]) map.get(PROP_LINK_IDS);
274 if (linkIdArray != null) {
275 this.linkIds.clear();
276 this.linkIds.addAll(Arrays.asList(linkIdArray));
277 isLinkIdsModified = false;
278 } else {
279 // trigger write on next serialize
280 isLinkIdsModified = true;
281 }
282
283 if (this.deviceIds == null) {
284 this.deviceIds = new TreeSet<>(
285 ByteArrayComparator.BYTEARRAY_COMPARATOR);
286 }
287 byte[] deviceIdArray[] = (byte[][]) map.get(PROP_DEVICE_IDS);
288 if (deviceIdArray != null) {
289 this.deviceIds.clear();
290 this.deviceIds.addAll(Arrays.asList(deviceIdArray));
291 isDeviceIdsModified = false;
292 } else {
293 // trigger write on next serialize
294 isDeviceIdsModified = true;
295 }
296
297 if (log.isWarnEnabled() && (linkIds.size() * deviceIds.size()) != 0) {
298 log.warn("Either #LinkIds:{} or #DeviceIds:{} is expected to be 0",
299 linkIds.size(), deviceIds.size());
300 }
301
302 return map;
303 }
304
305 @Override
306 public String toString() {
307 // TODO OUTPUT ALL?
308 return "[RCPort 0x" + Long.toHexString(dpid) + "@" + number
309 + " STATUS:" + status + "]";
310 }
311
312 public static void main(String[] args) {
313 // TODO Auto-generated method stub
314
315 }
316
317}