Thomas Vachuska
Committed by Ray Milkey

Fixed an issue in basic host and basic link config validations.

Added validation for basic device config.

Made all concrete configurations final (as they should be).

Changed stc to use the new onos-netcfg as a method to re-locate and name devices and hosts.

Change-Id: I372e6c7e6c0fa6fa52301568af73342aaae6347b
...@@ -95,15 +95,15 @@ public abstract class Config<S> { ...@@ -95,15 +95,15 @@ public abstract class Config<S> {
95 * <p> 95 * <p>
96 * Default implementation returns true. 96 * Default implementation returns true.
97 * Subclasses are expected to override this with their own validation. 97 * Subclasses are expected to override this with their own validation.
98 - * </p> 98 + * Implementations are free to throw a RuntimeException if data is invalid.
99 + * * </p>
99 * 100 *
100 * @return true if the data is valid; false otherwise 101 * @return true if the data is valid; false otherwise
102 + * @throws RuntimeException if configuration is invalid or completely foobar
101 */ 103 */
102 public boolean isValid() { 104 public boolean isValid() {
103 - // TODO: figure out what assertions could be made in the base class 105 + // Derivatives should use the provided set of predicates to test
104 - // NOTE: The thought is to have none, but instead to provide a set 106 + // validity of their fields, e.g.:
105 - // of predicates to allow configs to test validity of present fields,
106 - // e.g.:
107 // isString(path) 107 // isString(path)
108 // isBoolean(path) 108 // isBoolean(path)
109 // isNumber(path, [min, max]) 109 // isNumber(path, [min, max])
......
...@@ -23,7 +23,7 @@ import org.onosproject.net.config.Config; ...@@ -23,7 +23,7 @@ import org.onosproject.net.config.Config;
23 */ 23 */
24 public abstract class AllowedEntityConfig<S> extends Config<S> { 24 public abstract class AllowedEntityConfig<S> extends Config<S> {
25 25
26 - private static final String ALLOWED = "allowed"; 26 + protected static final String ALLOWED = "allowed";
27 27
28 /** 28 /**
29 * Indicates whether the element is allowed for admission into the control 29 * Indicates whether the element is allowed for admission into the control
......
...@@ -21,11 +21,17 @@ import org.onosproject.net.DeviceId; ...@@ -21,11 +21,17 @@ import org.onosproject.net.DeviceId;
21 /** 21 /**
22 * Basic configuration for network infrastructure devices. 22 * Basic configuration for network infrastructure devices.
23 */ 23 */
24 -public class BasicDeviceConfig extends BasicElementConfig<DeviceId> { 24 +public final class BasicDeviceConfig extends BasicElementConfig<DeviceId> {
25 25
26 - public static final String TYPE = "type"; 26 + private static final String TYPE = "type";
27 - public static final String DRIVER = "driver"; 27 + private static final String DRIVER = "driver";
28 - public static final String MANAGEMENT_ADDRESS = "managementAddress"; 28 + private static final String MANAGEMENT_ADDRESS = "managementAddress";
29 +
30 + @Override
31 + public boolean isValid() {
32 + return hasOnlyFields(ALLOWED, NAME, LATITUDE, LONGITUDE, RACK_ADDRESS, OWNER,
33 + TYPE, DRIVER, MANAGEMENT_ADDRESS);
34 + }
29 35
30 /** 36 /**
31 * Returns the device type. 37 * Returns the device type.
...@@ -85,6 +91,6 @@ public class BasicDeviceConfig extends BasicElementConfig<DeviceId> { ...@@ -85,6 +91,6 @@ public class BasicDeviceConfig extends BasicElementConfig<DeviceId> {
85 } 91 }
86 92
87 // TODO: device port meta-data to be configured via BasicPortsConfig 93 // TODO: device port meta-data to be configured via BasicPortsConfig
88 - // TODO: device credentials/keys 94 + // TODO: device credentials/keys; in a separate config
89 95
90 } 96 }
......
...@@ -22,13 +22,13 @@ package org.onosproject.net.config.basics; ...@@ -22,13 +22,13 @@ package org.onosproject.net.config.basics;
22 */ 22 */
23 public abstract class BasicElementConfig<S> extends AllowedEntityConfig<S> { 23 public abstract class BasicElementConfig<S> extends AllowedEntityConfig<S> {
24 24
25 - public static final String NAME = "name"; 25 + protected static final String NAME = "name";
26 26
27 - public static final String LATITUDE = "latitude"; 27 + protected static final String LATITUDE = "latitude";
28 - public static final String LONGITUDE = "longitude"; 28 + protected static final String LONGITUDE = "longitude";
29 29
30 - public static final String RACK_ADDRESS = "rackAddress"; 30 + protected static final String RACK_ADDRESS = "rackAddress";
31 - public static final String OWNER = "owner"; 31 + protected static final String OWNER = "owner";
32 32
33 protected static final double DEFAULT_COORD = -1.0; 33 protected static final double DEFAULT_COORD = -1.0;
34 34
......
...@@ -19,68 +19,62 @@ import com.fasterxml.jackson.databind.node.ArrayNode; ...@@ -19,68 +19,62 @@ import com.fasterxml.jackson.databind.node.ArrayNode;
19 import org.onlab.packet.IpAddress; 19 import org.onlab.packet.IpAddress;
20 import org.onosproject.net.ConnectPoint; 20 import org.onosproject.net.ConnectPoint;
21 import org.onosproject.net.HostId; 21 import org.onosproject.net.HostId;
22 +
22 import java.util.HashSet; 23 import java.util.HashSet;
23 import java.util.Set; 24 import java.util.Set;
24 25
26 +import static org.onosproject.net.config.basics.AllowedEntityConfig.ALLOWED;
27 +
25 /** 28 /**
26 * Basic configuration for network end-station hosts. 29 * Basic configuration for network end-station hosts.
27 */ 30 */
28 -public class BasicHostConfig extends BasicElementConfig<HostId> { 31 +public final class BasicHostConfig extends BasicElementConfig<HostId> {
32 +
29 private static final String IPS = "ips"; 33 private static final String IPS = "ips";
30 private static final String LOCATION = "location"; 34 private static final String LOCATION = "location";
31 35
32 @Override 36 @Override
33 public boolean isValid() { 37 public boolean isValid() {
34 - return hasOnlyFields(IPS, LOCATION) && 38 + // Location and IP addresses can be absent, but if present must be valid.
35 - this.location() != null && 39 + this.location();
36 - this.ipAddresses() != null; 40 + this.ipAddresses();
41 + return hasOnlyFields(ALLOWED, NAME, LATITUDE, LONGITUDE, RACK_ADDRESS, OWNER,
42 + IPS, LOCATION);
37 } 43 }
38 44
39 /** 45 /**
40 - * Gets location of the host. 46 + * Returns location of the host.
41 * 47 *
42 - * @return location of the host. Or null if not specified with correct format. 48 + * @return location of the host or null if not set
49 + * @throws IllegalArgumentException if not specified with correct format
43 */ 50 */
44 public ConnectPoint location() { 51 public ConnectPoint location() {
45 String location = get(LOCATION, null); 52 String location = get(LOCATION, null);
46 - 53 + return location != null ? ConnectPoint.deviceConnectPoint(location) : null;
47 - if (location != null) {
48 - try {
49 - return ConnectPoint.deviceConnectPoint(location);
50 - } catch (Exception e) {
51 - return null;
52 - }
53 - }
54 - return null;
55 } 54 }
56 55
57 /** 56 /**
58 * Sets the location of the host. 57 * Sets the location of the host.
59 * 58 *
60 - * @param location location of the host. 59 + * @param location location of the host or null to unset
61 - * @return the config of the host. 60 + * @return the config of the host
62 */ 61 */
63 public BasicHostConfig setLocation(String location) { 62 public BasicHostConfig setLocation(String location) {
64 return (BasicHostConfig) setOrClear(LOCATION, location); 63 return (BasicHostConfig) setOrClear(LOCATION, location);
65 } 64 }
66 65
67 /** 66 /**
68 - * Gets IP addresses of the host. 67 + * Returns IP addresses of the host.
69 * 68 *
70 - * @return IP addresses of the host. Or null if not specified with correct format. 69 + * @return IP addresses of the host or null if not set
70 + * @throws IllegalArgumentException if not specified with correct format
71 */ 71 */
72 public Set<IpAddress> ipAddresses() { 72 public Set<IpAddress> ipAddresses() {
73 HashSet<IpAddress> ipAddresses = new HashSet<>(); 73 HashSet<IpAddress> ipAddresses = new HashSet<>();
74 if (object.has(IPS)) { 74 if (object.has(IPS)) {
75 ArrayNode ipNodes = (ArrayNode) object.path(IPS); 75 ArrayNode ipNodes = (ArrayNode) object.path(IPS);
76 - try { 76 + ipNodes.forEach(n -> ipAddresses.add(IpAddress.valueOf(n.asText())));
77 - ipNodes.forEach(ipNode -> { 77 + return ipAddresses;
78 - ipAddresses.add(IpAddress.valueOf(ipNode.asText()));
79 - });
80 - return ipAddresses;
81 - } catch (Exception e) {
82 - return null;
83 - }
84 } 78 }
85 return null; 79 return null;
86 } 80 }
...@@ -88,8 +82,8 @@ public class BasicHostConfig extends BasicElementConfig<HostId> { ...@@ -88,8 +82,8 @@ public class BasicHostConfig extends BasicElementConfig<HostId> {
88 /** 82 /**
89 * Sets the IP addresses of the host. 83 * Sets the IP addresses of the host.
90 * 84 *
91 - * @param ipAddresses IP addresses of the host. 85 + * @param ipAddresses IP addresses of the host or null to unset
92 - * @return the config of the host. 86 + * @return the config of the host
93 */ 87 */
94 public BasicHostConfig setIps(Set<IpAddress> ipAddresses) { 88 public BasicHostConfig setIps(Set<IpAddress> ipAddresses) {
95 return (BasicHostConfig) setOrClear(IPS, ipAddresses); 89 return (BasicHostConfig) setOrClear(IPS, ipAddresses);
......
...@@ -26,19 +26,19 @@ import static org.onosproject.net.config.Config.FieldPresence.OPTIONAL; ...@@ -26,19 +26,19 @@ import static org.onosproject.net.config.Config.FieldPresence.OPTIONAL;
26 /** 26 /**
27 * Basic configuration for network infrastructure link. 27 * Basic configuration for network infrastructure link.
28 */ 28 */
29 -public class BasicLinkConfig extends AllowedEntityConfig<LinkKey> { 29 +public final class BasicLinkConfig extends AllowedEntityConfig<LinkKey> {
30 30
31 - public static final String TYPE = "type"; 31 + private static final String TYPE = "type";
32 - public static final String METRIC = "metric"; 32 + private static final String METRIC = "metric";
33 - public static final String LATENCY = "latency"; 33 + private static final String LATENCY = "latency";
34 - public static final String BANDWIDTH = "bandwidth"; 34 + private static final String BANDWIDTH = "bandwidth";
35 - public static final String IS_DURABLE = "durable"; 35 + private static final String IS_DURABLE = "durable";
36 36
37 @Override 37 @Override
38 public boolean isValid() { 38 public boolean isValid() {
39 - return hasOnlyFields(TYPE, METRIC, LATENCY, BANDWIDTH, IS_DURABLE) && 39 + return hasOnlyFields(ALLOWED, TYPE, METRIC, LATENCY, BANDWIDTH, IS_DURABLE) &&
40 - isNumber(METRIC, OPTIONAL) && isNumber(LATENCY, OPTIONAL) && 40 + isBoolean(ALLOWED, OPTIONAL) && isNumber(METRIC, OPTIONAL) &&
41 - isNumber(BANDWIDTH, OPTIONAL); 41 + isNumber(LATENCY, OPTIONAL) && isNumber(BANDWIDTH, OPTIONAL);
42 } 42 }
43 43
44 /** 44 /**
......
...@@ -23,11 +23,13 @@ import org.onosproject.net.Port; ...@@ -23,11 +23,13 @@ import org.onosproject.net.Port;
23 23
24 import com.fasterxml.jackson.databind.JsonNode; 24 import com.fasterxml.jackson.databind.JsonNode;
25 25
26 +import static org.onosproject.net.config.Config.FieldPresence.OPTIONAL;
27 +
26 28
27 /** 29 /**
28 * Configurations for an optical port on a device. 30 * Configurations for an optical port on a device.
29 */ 31 */
30 -public class OpticalPortConfig extends Config<ConnectPoint> { 32 +public final class OpticalPortConfig extends Config<ConnectPoint> {
31 // optical type {OMS, OCH, ODUClt, fiber} 33 // optical type {OMS, OCH, ODUClt, fiber}
32 public static final String TYPE = "type"; 34 public static final String TYPE = "type";
33 35
...@@ -42,6 +44,12 @@ public class OpticalPortConfig extends Config<ConnectPoint> { ...@@ -42,6 +44,12 @@ public class OpticalPortConfig extends Config<ConnectPoint> {
42 // **Linc-OE : remove if it's not needed after all.** 44 // **Linc-OE : remove if it's not needed after all.**
43 public static final String SPEED = "speed"; 45 public static final String SPEED = "speed";
44 46
47 + @Override
48 + public boolean isValid() {
49 + return hasOnlyFields(TYPE, NAME, PORT, STATIC_PORT, STATIC_LAMBDA, SPEED) &&
50 + isNumber(STATIC_LAMBDA, OPTIONAL) && isNumber(SPEED, OPTIONAL);
51 + }
52 +
45 /** 53 /**
46 * Returns the Enum value representing the type of port. 54 * Returns the Enum value representing the type of port.
47 * 55 *
......
...@@ -32,7 +32,7 @@ import static org.slf4j.LoggerFactory.getLogger; ...@@ -32,7 +32,7 @@ import static org.slf4j.LoggerFactory.getLogger;
32 32
33 /** 33 /**
34 * Implementations of merge policies for various sources of device configuration 34 * Implementations of merge policies for various sources of device configuration
35 - * information. This includes applications, provides, and network configurations. 35 + * information. This includes applications, providers, and network configurations.
36 */ 36 */
37 public final class BasicDeviceOperator implements ConfigOperator { 37 public final class BasicDeviceOperator implements ConfigOperator {
38 38
......
...@@ -15,25 +15,26 @@ ...@@ -15,25 +15,26 @@
15 */ 15 */
16 package org.onosproject.net.host.impl; 16 package org.onosproject.net.host.impl;
17 17
18 -import static org.slf4j.LoggerFactory.getLogger; 18 +import org.onlab.packet.IpAddress;
19 -
20 -import org.slf4j.Logger;
21 -import org.onosproject.net.config.ConfigOperator;
22 -import org.onosproject.net.config.basics.BasicHostConfig;
23 import org.onosproject.net.AnnotationKeys; 19 import org.onosproject.net.AnnotationKeys;
20 +import org.onosproject.net.ConnectPoint;
24 import org.onosproject.net.DefaultAnnotations; 21 import org.onosproject.net.DefaultAnnotations;
22 +import org.onosproject.net.HostLocation;
25 import org.onosproject.net.SparseAnnotations; 23 import org.onosproject.net.SparseAnnotations;
24 +import org.onosproject.net.config.ConfigOperator;
25 +import org.onosproject.net.config.basics.BasicHostConfig;
26 import org.onosproject.net.host.DefaultHostDescription; 26 import org.onosproject.net.host.DefaultHostDescription;
27 import org.onosproject.net.host.HostDescription; 27 import org.onosproject.net.host.HostDescription;
28 28
29 +import java.util.Set;
30 +
29 /** 31 /**
30 * Implementations of merge policies for various sources of host configuration 32 * Implementations of merge policies for various sources of host configuration
31 - * information. This includes applications, provides, and network configurations. 33 + * information. This includes applications, providers, and network configurations.
32 */ 34 */
33 public final class BasicHostOperator implements ConfigOperator { 35 public final class BasicHostOperator implements ConfigOperator {
34 36
35 protected static final double DEFAULT_COORD = -1.0; 37 protected static final double DEFAULT_COORD = -1.0;
36 - private static final Logger log = getLogger(BasicHostOperator.class);
37 38
38 private BasicHostOperator() { 39 private BasicHostOperator() {
39 } 40 }
...@@ -42,7 +43,7 @@ public final class BasicHostOperator implements ConfigOperator { ...@@ -42,7 +43,7 @@ public final class BasicHostOperator implements ConfigOperator {
42 * Generates a HostDescription containing fields from a HostDescription and 43 * Generates a HostDescription containing fields from a HostDescription and
43 * a HostConfig. 44 * a HostConfig.
44 * 45 *
45 - * @param cfg the host config entity from network config 46 + * @param cfg the host config entity from network config
46 * @param descr a HostDescription 47 * @param descr a HostDescription
47 * @return HostDescription based on both sources 48 * @return HostDescription based on both sources
48 */ 49 */
...@@ -50,16 +51,29 @@ public final class BasicHostOperator implements ConfigOperator { ...@@ -50,16 +51,29 @@ public final class BasicHostOperator implements ConfigOperator {
50 if (cfg == null) { 51 if (cfg == null) {
51 return descr; 52 return descr;
52 } 53 }
54 +
55 + HostLocation location = descr.location();
56 + ConnectPoint cfgLocation = cfg.location();
57 + if (cfgLocation != null) {
58 + location = new HostLocation(cfgLocation, System.currentTimeMillis());
59 + }
60 +
61 + Set<IpAddress> ipAddresses = descr.ipAddress();
62 + Set<IpAddress> cfgIpAddresses = cfg.ipAddresses();
63 + if (cfgIpAddresses != null) {
64 + ipAddresses = cfgIpAddresses;
65 + }
66 +
53 SparseAnnotations sa = combine(cfg, descr.annotations()); 67 SparseAnnotations sa = combine(cfg, descr.annotations());
54 - return new DefaultHostDescription(descr.hwAddress(), descr.vlan(), descr.location(), 68 + return new DefaultHostDescription(descr.hwAddress(), descr.vlan(),
55 - descr.ipAddress(), sa); 69 + location, ipAddresses, sa);
56 } 70 }
57 71
58 /** 72 /**
59 * Generates an annotation from an existing annotation and HostConfig. 73 * Generates an annotation from an existing annotation and HostConfig.
60 * 74 *
61 * @param cfg the device config entity from network config 75 * @param cfg the device config entity from network config
62 - * @param an the annotation 76 + * @param an the annotation
63 * @return annotation combining both sources 77 * @return annotation combining both sources
64 */ 78 */
65 public static SparseAnnotations combine(BasicHostConfig cfg, SparseAnnotations an) { 79 public static SparseAnnotations combine(BasicHostConfig cfg, SparseAnnotations an) {
......
...@@ -192,4 +192,4 @@ function vicell { ...@@ -192,4 +192,4 @@ function vicell {
192 192
193 193
194 # Load AT&T MPLS topo GEO data 194 # Load AT&T MPLS topo GEO data
195 -alias atttopo='onos-topo-cfg $OCI $ONOS_ROOT/tools/test/topos/attmpls.json'
...\ No newline at end of file ...\ No newline at end of file
195 +alias atttopo='onos-netcfg $OCI $ONOS_ROOT/tools/test/topos/attmpls-cfg.json'
...\ No newline at end of file ...\ No newline at end of file
......
...@@ -10,6 +10,6 @@ node="${1:-$OCI}" ...@@ -10,6 +10,6 @@ node="${1:-$OCI}"
10 file="${2:-$ONOS_ROOT/tools/test/topos/oe-linear-3.json}" 10 file="${2:-$ONOS_ROOT/tools/test/topos/oe-linear-3.json}"
11 url="${3}" 11 url="${3}"
12 12
13 -curl -sS --fail -L --user $ONOS_WEB_USER:$ONOS_WEB_PASS \ 13 +curl --fail -L --user $ONOS_WEB_USER:$ONOS_WEB_PASS \
14 -X POST -H 'Content-Type:application/json' \ 14 -X POST -H 'Content-Type:application/json' \
15 http://$node:8181/onos/v1/network/configuration/${url} -d@$file 15 http://$node:8181/onos/v1/network/configuration/${url} -d@$file
......
...@@ -30,8 +30,5 @@ ...@@ -30,8 +30,5 @@
30 30
31 <step name="Check-Summary-For-Hosts" requires="~Ping-All-And-Verify" 31 <step name="Check-Summary-For-Hosts" requires="~Ping-All-And-Verify"
32 exec="onos-check-summary ${OC1} [0-9]* 25 140 25"/> 32 exec="onos-check-summary ${OC1} [0-9]* 25 140 25"/>
33 -
34 - <step name="Config-Topo" requires="~Check-Summary-For-Hosts"
35 - exec="onos-topo-cfg ${OC1} ${ONOS_ROOT}/tools/test/topos/attmpls.json"/>
36 </group> 33 </group>
37 </scenario> 34 </scenario>
......
...@@ -24,8 +24,11 @@ ...@@ -24,8 +24,11 @@
24 <step name="Initial-Summary-Check" requires="~Wipe-Out-Data-Before" 24 <step name="Initial-Summary-Check" requires="~Wipe-Out-Data-Before"
25 exec="onos-check-summary ${OC1} [0-9]* 0 0 0"/> 25 exec="onos-check-summary ${OC1} [0-9]* 0 0 0"/>
26 26
27 + <step name="Config-Topo" requires="~Initial-Summary-Check"
28 + exec="onos-netcfg ${OC1} ${ONOS_ROOT}/tools/test/topos/attmpls-cfg.json"/>
29 +
27 <step name="Start-Mininet" 30 <step name="Start-Mininet"
28 - requires="Install-Apps,Initial-Summary-Check,Push-Topos,Stop-Mininet-If-Needed" 31 + requires="Install-Apps,Config-Topo,Push-Topos,Stop-Mininet-If-Needed"
29 exec="onos-mininet start topos/topo att-onos.py ${ONOS_INSTANCES}"/> 32 exec="onos-mininet start topos/topo att-onos.py ${ONOS_INSTANCES}"/>
30 33
31 <step name="Wait-For-Mininet" requires="Start-Mininet" 34 <step name="Wait-For-Mininet" requires="Start-Mininet"
......
1 +{
2 + "devices": {
3 + "of:0000000000000001": { "basic": { "name": "CMBR", "latitude": 42.373730, "longitude": -71.109734 }},
4 + "of:0000000000000002": { "basic": { "name": "CHCG", "latitude": 41.877461, "longitude": -87.642892 }},
5 + "of:0000000000000003": { "basic": { "name": "CLEV", "latitude": 41.498928, "longitude": -81.695217 }},
6 + "of:0000000000000004": { "basic": { "name": "RLGH", "latitude": 35.780150, "longitude": -78.644026 }},
7 + "of:0000000000000005": { "basic": { "name": "ATLN", "latitude": 33.749017, "longitude": -84.394168 }},
8 + "of:0000000000000006": { "basic": { "name": "PHLA", "latitude": 39.952906, "longitude": -75.172278 }},
9 + "of:0000000000000007": { "basic": { "name": "WASH", "latitude": 38.906696, "longitude": -77.035509 }},
10 + "of:0000000000000008": { "basic": { "name": "NSVL", "latitude": 36.166410, "longitude": -86.787305 }},
11 + "of:0000000000000009": { "basic": { "name": "STLS", "latitude": 38.626418, "longitude": -90.198143 }},
12 + "of:000000000000000a": { "basic": { "name": "NWOR", "latitude": 29.951475, "longitude": -90.078434 }},
13 + "of:000000000000000b": { "basic": { "name": "HSTN", "latitude": 29.763249, "longitude": -95.368332 }},
14 + "of:000000000000000c": { "basic": { "name": "SNAN", "latitude": 29.424331, "longitude": -98.491745 }},
15 + "of:000000000000000d": { "basic": { "name": "DLLS", "latitude": 32.777665, "longitude": -96.802064 }},
16 + "of:000000000000000e": { "basic": { "name": "ORLD", "latitude": 28.538641, "longitude": -81.381110 }},
17 + "of:000000000000000f": { "basic": { "name": "DNVR", "latitude": 39.736623, "longitude": -104.984887 }},
18 + "of:0000000000000010": { "basic": { "name": "KSCY", "latitude": 39.100725, "longitude": -94.581228 }},
19 + "of:0000000000000011": { "basic": { "name": "SNFN", "latitude": 37.779751, "longitude": -122.409791 }},
20 + "of:0000000000000012": { "basic": { "name": "SCRM", "latitude": 38.581001, "longitude": -121.497844 }},
21 + "of:0000000000000013": { "basic": { "name": "PTLD", "latitude": 45.523317, "longitude": -122.677768 }},
22 + "of:0000000000000014": { "basic": { "name": "STTL", "latitude": 47.607326, "longitude": -122.331786 }},
23 + "of:0000000000000015": { "basic": { "name": "SLKC", "latitude": 40.759577, "longitude": -111.895079 }},
24 + "of:0000000000000016": { "basic": { "name": "LA03", "latitude": 34.056346, "longitude": -118.235951 }},
25 + "of:0000000000000017": { "basic": { "name": "SNDG", "latitude": 32.714564, "longitude": -117.153528 }},
26 + "of:0000000000000018": { "basic": { "name": "PHNX", "latitude": 33.448289, "longitude": -112.076299 }},
27 + "of:0000000000000019": { "basic": { "name": "NY54", "latitude": 40.728270, "longitude": -73.994483 }}
28 + },
29 +
30 + "hosts": {
31 + "00:00:00:00:00:01/-1": { "basic": { "location": "of:0000000000000001/1", "ips": [ "10.0.0.1" ], "name": "CMBR", "latitude": 43.355715, "longitude": -69.528243 }},
32 + "00:00:00:00:00:02/-1": { "basic": { "location": "of:0000000000000002/1", "ips": [ "10.0.0.2" ], "name": "CHCG", "latitude": 43.632679, "longitude": -88.772526 }},
33 + "00:00:00:00:00:03/-1": { "basic": { "location": "of:0000000000000003/1", "ips": [ "10.0.0.3" ], "name": "CLEV", "latitude": 42.756945, "longitude": -79.831317 }},
34 + "00:00:00:00:00:04/-1": { "basic": { "location": "of:0000000000000004/1", "ips": [ "10.0.0.4" ], "name": "RLGH", "latitude": 36.972249, "longitude": -76.667163 }},
35 + "00:00:00:00:00:05/-1": { "basic": { "location": "of:0000000000000005/1", "ips": [ "10.0.0.5" ], "name": "ATLN", "latitude": 35.427493, "longitude": -83.885831 }},
36 + "00:00:00:00:00:06/-1": { "basic": { "location": "of:0000000000000006/1", "ips": [ "10.0.0.6" ], "name": "PHLA", "latitude": 39.208113, "longitude": -73.421341 }},
37 + "00:00:00:00:00:07/-1": { "basic": { "location": "of:0000000000000007/1", "ips": [ "10.0.0.7" ], "name": "WASH", "latitude": 40.133860, "longitude": -79.238299 }},
38 + "00:00:00:00:00:08/-1": { "basic": { "location": "of:0000000000000008/1", "ips": [ "10.0.0.8" ], "name": "NSVL", "latitude": 37.407589, "longitude": -84.415068 }},
39 + "00:00:00:00:00:09/-1": { "basic": { "location": "of:0000000000000009/1", "ips": [ "10.0.0.9" ], "name": "STLS", "latitude": 40.066810, "longitude": -90.932405 }},
40 + "00:00:00:00:00:0a/-1": { "basic": { "location": "of:000000000000000a/1", "ips": [ "10.0.0.10" ], "name": "NWOR", "latitude": 31.470982, "longitude": -88.779353 }},
41 + "00:00:00:00:00:0b/-1": { "basic": { "location": "of:000000000000000b/1", "ips": [ "10.0.0.11" ], "name": "HSTN", "latitude": 31.136858, "longitude": -94.351656 }},
42 + "00:00:00:00:00:0c/-1": { "basic": { "location": "of:000000000000000c/1", "ips": [ "10.0.0.12" ], "name": "SNAN", "latitude": 28.040975, "longitude": -99.169527 }},
43 + "00:00:00:00:00:0d/-1": { "basic": { "location": "of:000000000000000d/1", "ips": [ "10.0.0.13" ], "name": "DLLS", "latitude": 31.899825, "longitude": -99.287263 }},
44 + "00:00:00:00:00:0e/-1": { "basic": { "location": "of:000000000000000e/1", "ips": [ "10.0.0.14" ], "name": "ORLD", "latitude": 26.670509, "longitude": -81.291920 }},
45 + "00:00:00:00:00:0f/-1": { "basic": { "location": "of:000000000000000f/1", "ips": [ "10.0.0.15" ], "name": "DNVR", "latitude": 40.888148, "longitude": -103.459878 }},
46 + "00:00:00:00:00:10/-1": { "basic": { "location": "of:0000000000000010/1", "ips": [ "10.0.0.16" ], "name": "KSCY", "latitude": 40.545088, "longitude": -93.734002 }},
47 + "00:00:00:00:00:11/-1": { "basic": { "location": "of:0000000000000011/1", "ips": [ "10.0.0.17" ], "name": "SNFN", "latitude": 39.081743, "longitude": -124.330172 }},
48 + "00:00:00:00:00:12/-1": { "basic": { "location": "of:0000000000000012/1", "ips": [ "10.0.0.18" ], "name": "SCRM", "latitude": 40.107468, "longitude": -120.424689 }},
49 + "00:00:00:00:00:13/-1": { "basic": { "location": "of:0000000000000013/1", "ips": [ "10.0.0.19" ], "name": "PTLD", "latitude": 44.383051, "longitude": -124.767594 }},
50 + "00:00:00:00:00:14/-1": { "basic": { "location": "of:0000000000000014/1", "ips": [ "10.0.0.20" ], "name": "STTL", "latitude": 48.832627, "longitude": -120.298441 }},
51 + "00:00:00:00:00:15/-1": { "basic": { "location": "of:0000000000000015/1", "ips": [ "10.0.0.21" ], "name": "SLKC", "latitude": 42.301734, "longitude": -111.217297 }},
52 + "00:00:00:00:00:16/-1": { "basic": { "location": "of:0000000000000016/1", "ips": [ "10.0.0.22" ], "name": "LA03", "latitude": 33.224634, "longitude": -121.532943 }},
53 + "00:00:00:00:00:17/-1": { "basic": { "location": "of:0000000000000017/1", "ips": [ "10.0.0.23" ], "name": "SNDG", "latitude": 31.834607, "longitude": -118.847982 }},
54 + "00:00:00:00:00:18/-1": { "basic": { "location": "of:0000000000000018/1", "ips": [ "10.0.0.24" ], "name": "PHNX", "latitude": 34.662290, "longitude": -110.946662 }},
55 + "00:00:00:00:00:19/-1": { "basic": { "location": "of:0000000000000019/1", "ips": [ "10.0.0.25" ], "name": "NY54", "latitude": 42.395459, "longitude": -75.293563 }}
56 + }
57 +}