Committed by
Gerrit Code Review
Revert "OECF removed working"
This reverts commit 5f8f8f0c. Change-Id: I13207976c26fc210994c90e213349790a4227440
Showing
4 changed files
with
746 additions
and
382 deletions
apps/oecfg/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 | + <parent> | ||
23 | + <groupId>org.onosproject</groupId> | ||
24 | + <artifactId>onos-apps</artifactId> | ||
25 | + <version>1.3.0-SNAPSHOT</version> | ||
26 | + <relativePath>../pom.xml</relativePath> | ||
27 | + </parent> | ||
28 | + | ||
29 | + <artifactId>onos-app-oecfg</artifactId> | ||
30 | + <packaging>jar</packaging> | ||
31 | + | ||
32 | + <description>Standalone utility for converting ONOS JSON config to OE-Linc JSON config</description> | ||
33 | + | ||
34 | + <dependencies> | ||
35 | + <dependency> | ||
36 | + <groupId>com.fasterxml.jackson.core</groupId> | ||
37 | + <artifactId>jackson-databind</artifactId> | ||
38 | + <scope>compile</scope> | ||
39 | + </dependency> | ||
40 | + <dependency> | ||
41 | + <groupId>com.fasterxml.jackson.core</groupId> | ||
42 | + <artifactId>jackson-annotations</artifactId> | ||
43 | + <scope>compile</scope> | ||
44 | + </dependency> | ||
45 | + </dependencies> | ||
46 | + | ||
47 | + <build> | ||
48 | + <plugins> | ||
49 | + <plugin> | ||
50 | + <groupId>org.apache.maven.plugins</groupId> | ||
51 | + <artifactId>maven-shade-plugin</artifactId> | ||
52 | + <executions> | ||
53 | + <execution> | ||
54 | + <phase>package</phase> | ||
55 | + <goals> | ||
56 | + <goal>shade</goal> | ||
57 | + </goals> | ||
58 | + <configuration> | ||
59 | + <transformers> | ||
60 | + <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer"> | ||
61 | + <manifestEntries> | ||
62 | + <Main-Class>org.onosproject.oecfg.OELinkConfig</Main-Class> | ||
63 | + </manifestEntries> | ||
64 | + </transformer> | ||
65 | + </transformers> | ||
66 | + </configuration> | ||
67 | + </execution> | ||
68 | + </executions> | ||
69 | + </plugin> | ||
70 | + </plugins> | ||
71 | + </build> | ||
72 | + | ||
73 | +</project> |
1 | +/* | ||
2 | + * Copyright 2014 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.oecfg; | ||
17 | + | ||
18 | +import com.fasterxml.jackson.databind.JsonNode; | ||
19 | +import com.fasterxml.jackson.databind.ObjectMapper; | ||
20 | +import com.fasterxml.jackson.databind.node.ArrayNode; | ||
21 | +import com.fasterxml.jackson.databind.node.ObjectNode; | ||
22 | + | ||
23 | +import java.io.IOException; | ||
24 | +import java.io.InputStream; | ||
25 | +import java.util.HashMap; | ||
26 | +import java.util.Map; | ||
27 | + | ||
28 | +/** | ||
29 | + * Utility program to convert standard ONOS config JSON to format expected | ||
30 | + * by the OE Link switch. | ||
31 | + */ | ||
32 | +public final class OELinkConfig { | ||
33 | + | ||
34 | + private ObjectMapper mapper = new ObjectMapper(); | ||
35 | + private Map<String, String> dpidToName = new HashMap<>(); | ||
36 | + | ||
37 | + public static void main(String[] args) { | ||
38 | + try { | ||
39 | + OELinkConfig config = new OELinkConfig(); | ||
40 | + JsonNode json = config.convert(System.in); | ||
41 | + System.out.println(json.toString()); | ||
42 | + } catch (IOException e) { | ||
43 | + System.err.println("Unable to convert JSON due to: " + e.getMessage()); | ||
44 | + e.printStackTrace(); | ||
45 | + } | ||
46 | + } | ||
47 | + | ||
48 | + private OELinkConfig() { | ||
49 | + } | ||
50 | + | ||
51 | + private JsonNode convert(InputStream input) throws IOException { | ||
52 | + JsonNode json = mapper.readTree(input); | ||
53 | + ObjectNode result = mapper.createObjectNode(); | ||
54 | + result.set("switchConfig", opticalSwitches(json)); | ||
55 | + result.set("linkConfig", opticalLinks(json)); | ||
56 | + return result; | ||
57 | + } | ||
58 | + | ||
59 | + private JsonNode opticalSwitches(JsonNode json) { | ||
60 | + ArrayNode result = mapper.createArrayNode(); | ||
61 | + for (JsonNode node : json.get("devices")) { | ||
62 | + String dpid = dpid(node.path("uri")); | ||
63 | + String name = node.path("name").asText("none"); | ||
64 | + dpidToName.put(dpid, name); | ||
65 | + if (node.path("type").asText("none").equals("ROADM")) { | ||
66 | + result.add(opticalSwitch(dpid, name, (ObjectNode) node)); | ||
67 | + } | ||
68 | + } | ||
69 | + return result; | ||
70 | + } | ||
71 | + | ||
72 | + private ObjectNode opticalSwitch(String dpid, String name, ObjectNode node) { | ||
73 | + ObjectNode result = mapper.createObjectNode(); | ||
74 | + ObjectNode annot = (ObjectNode) node.path("annotations"); | ||
75 | + result.put("allowed", true).put("type", "Roadm") | ||
76 | + .put("name", name).put("nodeDpid", dpid) | ||
77 | + .put("latitude", annot.path("latitude").asDouble(0.0)) | ||
78 | + .put("longitude", annot.path("longitude").asDouble(0.0)) | ||
79 | + .set("params", switchParams(annot)); | ||
80 | + return result; | ||
81 | + } | ||
82 | + | ||
83 | + private ObjectNode switchParams(ObjectNode annot) { | ||
84 | + return mapper.createObjectNode() | ||
85 | + .put("numRegen", annot.path("optical.regens").asInt(0)); | ||
86 | + } | ||
87 | + | ||
88 | + private JsonNode opticalLinks(JsonNode json) { | ||
89 | + ArrayNode result = mapper.createArrayNode(); | ||
90 | + for (JsonNode node : json.get("links")) { | ||
91 | + if (node.path("type").asText("none").equals("OPTICAL")) { | ||
92 | + result.add(opticalLink((ObjectNode) node)); | ||
93 | + } | ||
94 | + } | ||
95 | + return result; | ||
96 | + } | ||
97 | + | ||
98 | + private ObjectNode opticalLink(ObjectNode node) { | ||
99 | + ObjectNode result = mapper.createObjectNode(); | ||
100 | + ObjectNode annot = (ObjectNode) node.path("annotations"); | ||
101 | + String src = dpid(node.path("src")); | ||
102 | + String dst = dpid(node.path("dst")); | ||
103 | + result.put("allowed", true).put("type", linkType(annot)) | ||
104 | + .put("nodeDpid1", src).put("nodeDpid2", dst) | ||
105 | + .set("params", linkParams(src, dst, node, annot)); | ||
106 | + return result; | ||
107 | + } | ||
108 | + | ||
109 | + private String linkType(ObjectNode annot) { | ||
110 | + return annot.path("optical.type").asText("cross-connect").equals("WDM") ? | ||
111 | + "wdmLink" : "pktOptLink"; | ||
112 | + } | ||
113 | + | ||
114 | + private ObjectNode linkParams(String src, String dst, | ||
115 | + ObjectNode node, ObjectNode annot) { | ||
116 | + ObjectNode result = mapper.createObjectNode() | ||
117 | + .put("nodeName1", dpidToName.get(src)) | ||
118 | + .put("nodeName2", dpidToName.get(dst)) | ||
119 | + .put("port1", port(node.path("src"))) | ||
120 | + .put("port2", port(node.path("dst"))); | ||
121 | + if (annot.has("bandwidth")) { | ||
122 | + result.put("bandwidth", annot.path("bandwidth").asInt()); | ||
123 | + } | ||
124 | + if (annot.has("optical.waves")) { | ||
125 | + result.put("numWaves", annot.path("optical.waves").asInt()); | ||
126 | + } | ||
127 | + return result; | ||
128 | + } | ||
129 | + | ||
130 | + private String dpid(JsonNode node) { | ||
131 | + String s = node.asText("of:0000000000000000").substring(3); | ||
132 | + return s.substring(0, 2) + ":" + s.substring(2, 4) + ":" + | ||
133 | + s.substring(4, 6) + ":" + s.substring(6, 8) + ":" + | ||
134 | + s.substring(8, 10) + ":" + s.substring(10, 12) + ":" + | ||
135 | + s.substring(12, 14) + ":" + s.substring(14, 16); | ||
136 | + } | ||
137 | + | ||
138 | + private int port(JsonNode node) { | ||
139 | + return Integer.parseInt(node.asText("of:0000000000000000/0").substring(20)); | ||
140 | + } | ||
141 | + | ||
142 | +} |
... | @@ -41,6 +41,7 @@ | ... | @@ -41,6 +41,7 @@ |
41 | <module>sdnip</module> | 41 | <module>sdnip</module> |
42 | <module>optical</module> | 42 | <module>optical</module> |
43 | <module>metrics</module> | 43 | <module>metrics</module> |
44 | + <module>oecfg</module> | ||
44 | <module>routing</module> | 45 | <module>routing</module> |
45 | <module>routing-api</module> | 46 | <module>routing-api</module> |
46 | <module>reactive-routing</module> | 47 | <module>reactive-routing</module> | ... | ... |
... | @@ -4,7 +4,7 @@ | ... | @@ -4,7 +4,7 @@ |
4 | Notes: | 4 | Notes: |
5 | 5 | ||
6 | This file contains classes and methods useful for integrating LincOE with Mininet, | 6 | This file contains classes and methods useful for integrating LincOE with Mininet, |
7 | -such as startOE, stopOE, OpticalLink, and OpticalSwitch | 7 | +such as startOE, stopOE, LINCLink, and OpticalSwitch |
8 | 8 | ||
9 | - $ONOS_ROOT ust be set | 9 | - $ONOS_ROOT ust be set |
10 | - Need to run with sudo -E to preserve ONOS_ROOT env var | 10 | - Need to run with sudo -E to preserve ONOS_ROOT env var |
... | @@ -22,10 +22,10 @@ such as startOE, stopOE, OpticalLink, and OpticalSwitch | ... | @@ -22,10 +22,10 @@ such as startOE, stopOE, OpticalLink, and OpticalSwitch |
22 | 22 | ||
23 | Usage: | 23 | Usage: |
24 | ------------ | 24 | ------------ |
25 | - - import OpticalLink and OpticalSwitch from this module | 25 | + - import LINCLink and OpticalSwitch from this module |
26 | - import startOE and stopOE from this module | 26 | - import startOE and stopOE from this module |
27 | - create topology as you would a normal topology. when | 27 | - create topology as you would a normal topology. when |
28 | - to an optical switch with topo.addLink, always specify cls=OpticalLink | 28 | + to an optical switch with topo.addLink, always specify cls=LINCLink |
29 | - when creating an optical switch, use cls=OpticalSwitch in topo.addSwitch | 29 | - when creating an optical switch, use cls=OpticalSwitch in topo.addSwitch |
30 | - for annotations on links and switches, a dictionary must be passed in as | 30 | - for annotations on links and switches, a dictionary must be passed in as |
31 | the annotations argument | 31 | the annotations argument |
... | @@ -51,11 +51,12 @@ switches have been started, the new Mininet start() method should also push the | ... | @@ -51,11 +51,12 @@ switches have been started, the new Mininet start() method should also push the |
51 | Topology configuration file to ONOS. | 51 | Topology configuration file to ONOS. |
52 | 52 | ||
53 | ''' | 53 | ''' |
54 | - | 54 | +import sys |
55 | import re | 55 | import re |
56 | import json | 56 | import json |
57 | import os | 57 | import os |
58 | from time import sleep | 58 | from time import sleep |
59 | +import urllib2 | ||
59 | 60 | ||
60 | from mininet.node import Switch, RemoteController | 61 | from mininet.node import Switch, RemoteController |
61 | from mininet.topo import Topo | 62 | from mininet.topo import Topo |
... | @@ -65,25 +66,127 @@ from mininet.log import setLogLevel, info, error, warn | ... | @@ -65,25 +66,127 @@ from mininet.log import setLogLevel, info, error, warn |
65 | from mininet.link import Link, Intf | 66 | from mininet.link import Link, Intf |
66 | from mininet.cli import CLI | 67 | from mininet.cli import CLI |
67 | 68 | ||
68 | -class OpticalSwitch( Switch ): | 69 | +# Sleep time and timeout values in seconds |
69 | - | 70 | +SLEEP_TIME = 2 |
70 | - def __init__( self, name, dpid=None, allowed=True, | 71 | +TIMEOUT = 60 |
71 | - switchType='ROADM', annotations={}, **params ): | 72 | + |
73 | +class OpticalSwitch(Switch): | ||
74 | + """ | ||
75 | + For now, same as Switch class. | ||
76 | + """ | ||
77 | + pass | ||
78 | + | ||
79 | +class OpticalIntf(Intf): | ||
80 | + """ | ||
81 | + For now,same as Intf class. | ||
82 | + """ | ||
83 | + pass | ||
84 | + | ||
85 | +class OpticalLink(Link): | ||
86 | + """ | ||
87 | + For now, same as Link. | ||
88 | + """ | ||
89 | + pass | ||
90 | + | ||
91 | +class LINCSwitch(OpticalSwitch): | ||
92 | + """ | ||
93 | + LINCSwitch class | ||
94 | + """ | ||
95 | + # FIXME:Sometimes LINC doesn't remove pipes and on restart increase the pipe | ||
96 | + # number from erlang.pipe.1.* to erlang.pipe.2.*, so should read and write | ||
97 | + # from latest pipe files. For now we are removing all the pipes before | ||
98 | + # starting LINC. | ||
99 | + ### User Name ### | ||
100 | + user = os.getlogin() | ||
101 | + ### pipes ### | ||
102 | + readPipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.r".format(user) | ||
103 | + writePipe = "/tmp/home/{}/linc-oe/rel/linc/erlang.pipe.1.w".format(user) | ||
104 | + ### sys.config path ### | ||
105 | + sysConfig = "/home/{}/linc-oe/rel/linc/releases/1.0/sys.config".format(user) | ||
106 | + ### method, mapping dpid to LINC switchId ### | ||
107 | + @staticmethod | ||
108 | + def dpids_to_ids(sysConfig): | ||
109 | + ''' | ||
110 | + return the dict containing switch dpids as key and LINC switch id as values | ||
111 | + ''' | ||
112 | + dpids_to_ids = {} | ||
113 | + fd = None | ||
114 | + try: | ||
115 | + with open(sysConfig, 'r', 0) as fd: | ||
116 | + switch_id = 1 | ||
117 | + for line in fd: | ||
118 | + dpid = re.search(r'([0-9A-Fa-f]{2}[:-]){7}([0-9A-Fa-f]{2})+', line, re.I) | ||
119 | + if dpid: | ||
120 | + dpids_to_ids[dpid.group().replace(':', '')] = switch_id | ||
121 | + switch_id += 1 | ||
122 | + return dpids_to_ids | ||
123 | + except: | ||
124 | + print "Error working with {}\nError: {}\n".format(sysConfig, sys.exc_info()) | ||
125 | + fd.close() | ||
126 | + return None | ||
127 | + ### dict of containing dpids as key and corresponding LINC switchId as values ### | ||
128 | + dpidsToLINCSwitchId = dpids_to_ids.__func__(sysConfig) | ||
129 | + @staticmethod | ||
130 | + def findDir(directory, userName): | ||
131 | + "finds and returns the path of any directory in the user's home directory" | ||
132 | + homeDir = '/home/' + userName | ||
133 | + Dir = quietRun('find %s -maxdepth 1 -name %s -type d' % (homeDir, directory)).strip('\n') | ||
134 | + DirList = Dir.split('\n') | ||
135 | + if not Dir: | ||
136 | + return None | ||
137 | + elif len(DirList) > 1 : | ||
138 | + warn('***WARNING: Found multiple instances of %s; using %s\n' | ||
139 | + % (directory, DirList[ 0 ])) | ||
140 | + return DirList[ 0 ] | ||
141 | + else: | ||
142 | + return Dir | ||
143 | + ### ONOS Directory ### | ||
144 | + try: | ||
145 | + onosDir = os.environ[ 'ONOS_ROOT' ] | ||
146 | + except: | ||
147 | + onosDir = findDir('onos', user) | ||
148 | + if not onosDir: | ||
149 | + error('Please set ONOS_ROOT environment variable!\n') | ||
150 | + else: | ||
151 | + os.environ[ 'ONOS_ROOT' ] = onosDir | ||
152 | + ### LINC-directory | ||
153 | + lincDir = findDir.__func__('linc-oe', user) | ||
154 | + if not lincDir: | ||
155 | + error("***ERROR: Could not find linc-oe in user's home directory\n") | ||
156 | + ### LINC config generator directory### | ||
157 | + configGen = findDir.__func__('LINC-config-generator', user) | ||
158 | + if not configGen: | ||
159 | + error("***ERROR: Could not find LINC-config-generator in user's home directory\n") | ||
160 | + # list of all the controllers | ||
161 | + controllers = None | ||
162 | + def __init__(self, name, dpid=None, allowed=True, | ||
163 | + switchType='ROADM', topo=None, annotations={}, controller=None, **params): | ||
72 | params[ 'inNamespace' ] = False | 164 | params[ 'inNamespace' ] = False |
73 | - Switch.__init__( self, name, dpid=dpid, **params ) | 165 | + Switch.__init__(self, name, dpid=dpid, **params) |
74 | self.name = name | 166 | self.name = name |
75 | self.annotations = annotations | 167 | self.annotations = annotations |
76 | self.allowed = allowed | 168 | self.allowed = allowed |
77 | self.switchType = switchType | 169 | self.switchType = switchType |
78 | - self.configDict = {} # dictionary that holds all of the JSON configuration data | 170 | + self.configDict = {} # dictionary that holds all of the JSON configuration data |
79 | - | 171 | + self.crossConnects = [] |
80 | - def start( self, *opts, **params ): | 172 | + self.deletedCrossConnects = [] |
173 | + self.controller = controller | ||
174 | + self.lincId = self._get_linc_id() # use to communicate with LINC | ||
175 | + self.lincStarted = False | ||
176 | + | ||
177 | + def start(self, *opts, **params): | ||
81 | '''Instead of starting a virtual switch, we build the JSON | 178 | '''Instead of starting a virtual switch, we build the JSON |
82 | dictionary for the emulated optical switch''' | 179 | dictionary for the emulated optical switch''' |
180 | + # TODO:Once LINC has the ability to spawn network element dynamically | ||
181 | + # we need to use this method to spawn new logical LINC switch rather then | ||
182 | + # bulding JSON. | ||
183 | + # if LINC is started then we can start and stop logical switches else create JSON | ||
184 | + if self.lincStarted: | ||
185 | + return self.start_oe() | ||
83 | self.configDict[ 'uri' ] = 'of:' + self.dpid | 186 | self.configDict[ 'uri' ] = 'of:' + self.dpid |
84 | self.configDict[ 'annotations' ] = self.annotations | 187 | self.configDict[ 'annotations' ] = self.annotations |
85 | - self.configDict[ 'annotations' ].setdefault( 'name', self.name ) | 188 | + self.configDict[ 'annotations' ].setdefault('name', self.name) |
86 | - self.configDict[ 'hw' ] = 'OE' | 189 | + self.configDict[ 'hw' ] = 'LINC-OE' |
87 | self.configDict[ 'mfr' ] = 'Linc' | 190 | self.configDict[ 'mfr' ] = 'Linc' |
88 | self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1] | 191 | self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1] |
89 | self.configDict[ 'type' ] = self.switchType | 192 | self.configDict[ 'type' ] = self.switchType |
... | @@ -92,74 +195,441 @@ class OpticalSwitch( Switch ): | ... | @@ -92,74 +195,441 @@ class OpticalSwitch( Switch ): |
92 | if intf.name == 'lo': | 195 | if intf.name == 'lo': |
93 | continue | 196 | continue |
94 | else: | 197 | else: |
95 | - self.configDict[ 'ports' ].append( intf.json() ) | 198 | + self.configDict[ 'ports' ].append(intf.json()) |
199 | + self.lincStarted = True | ||
200 | + | ||
201 | + def stop(self, deleteIntfs=False): | ||
202 | + ''' | ||
203 | + stop the existing switch | ||
204 | + ''' | ||
205 | + # TODO:Add support for deleteIntf | ||
206 | + self.stop_oe() | ||
207 | + | ||
208 | + def dpctl( self, *args ): | ||
209 | + "Run dpctl command: ignore for now" | ||
210 | + pass | ||
211 | + | ||
212 | + def write_to_cli(self, command): | ||
213 | + ''' | ||
214 | + send command to LINC | ||
215 | + ''' | ||
216 | + fd = None | ||
217 | + try: | ||
218 | + fd = open(self.writePipe, 'w', 0) | ||
219 | + fd.write(command) | ||
220 | + fd.close() | ||
221 | + except: | ||
222 | + print "Error working with {}\nError: {}\n".format(self.writePipe, sys.exc_info()) | ||
223 | + if fd: | ||
224 | + fd.close() | ||
225 | + | ||
226 | + def read_from_cli(self): | ||
227 | + ''' | ||
228 | + read the output from the LINC CLI | ||
229 | + ''' | ||
230 | + response = None | ||
231 | + fd = None | ||
232 | + try: | ||
233 | + fd = open(self.readPipe, 'r', 0) | ||
234 | + fcntl.fcntl(fd, fcntl.F_SETFL, os.O_NONBLOCK) # for non-blocking read | ||
235 | + # FIXME:Due to non-blocking read most for the time we read nothing | ||
236 | + response = fd.read() | ||
237 | + fd.close() | ||
238 | + except : | ||
239 | + # print "Error working with {}\nError: {}\n".format(self.readPipe, sys.exc_info()) | ||
240 | + if fd: | ||
241 | + fd.close() | ||
242 | + return response | ||
243 | + | ||
244 | + def _get_linc_id(self): | ||
245 | + ''' | ||
246 | + return the corresponding LINC switchId. | ||
247 | + ''' | ||
248 | + return LINCSwitch.dpidsToLINCSwitchId.get(self.dpid) | ||
249 | + #-------------------------------------------------------------------------- | ||
250 | + # LINC CLI commands | ||
251 | + #-------------------------------------------------------------------------- | ||
252 | + def start_oe(self): | ||
253 | + ''' | ||
254 | + start the existing LINC switch | ||
255 | + ''' | ||
256 | + #starting Switch | ||
257 | + cmd = "linc:start_switch({}).\r\n".format(self.lincId) | ||
258 | + self.write_to_cli(cmd) | ||
259 | + #hanlding taps interfaces related to the switch | ||
260 | + crossConnectJSON = {} | ||
261 | + linkConfig = [] | ||
262 | + for i in range(0,len(self.deletedCrossConnects)): | ||
263 | + crossConnect = self.deletedCrossConnects.pop() | ||
264 | + tap = None | ||
265 | + if isinstance(crossConnect.intf1.node, LINCSwitch): | ||
266 | + intf = crossConnect.intf2 | ||
267 | + tapPort = crossConnect.intf1.port | ||
268 | + else: | ||
269 | + intf = crossConnect.intf1 | ||
270 | + tapPort = crossConnect.intf2.port | ||
271 | + tap = LINCSwitch.findTap(self, tapPort) | ||
272 | + if tap: | ||
273 | + LINCSwitch.setupInts([tap]) | ||
274 | + intf.node.attach(tap) | ||
275 | + self.crossConnects.append(crossConnect) | ||
276 | + linkConfig.append(crossConnect.json()) | ||
277 | + #Sending crossConnect info to the ONOS. | ||
278 | + crossConnectJSON['links'] = linkConfig | ||
279 | + with open("crossConnect.json", 'w') as fd: | ||
280 | + json.dump(crossConnectJSON, fd, indent=4, separators=(',', ': ')) | ||
281 | + info('*** Pushing crossConnect.json to ONOS\n') | ||
282 | + output = quietRun('%s/tools/test/bin/onos-topo-cfg %s\ | ||
283 | + Topology.json' % (self.onosDir, self.controllers[ 0 ].ip), shell=True) | ||
284 | + | ||
285 | + def stop_oe(self): | ||
286 | + ''' | ||
287 | + stop the existing LINC switch | ||
288 | + ''' | ||
289 | + cmd = "linc:stop_switch({}).\r\n".format(self.lincId) | ||
290 | + self.write_to_cli(cmd) | ||
291 | + #handling taps if any | ||
292 | + for i in range(0, len(self.crossConnects)): | ||
293 | + crossConnect = self.crossConnects.pop() | ||
294 | + if isinstance(crossConnect.intf1.node, LINCSwitch): | ||
295 | + intf = crossConnect.intf2 | ||
296 | + tapPort = crossConnect.intf1.port | ||
297 | + else: | ||
298 | + intf = crossConnect.intf1 | ||
299 | + tapPort = crossConnect.intf2.port | ||
300 | + intf.node.detach(LINCSwitch.findTap(self, tapPort)) | ||
301 | + self.deletedCrossConnects.append(crossConnect) | ||
302 | + | ||
303 | + def w_port_up(self, port): | ||
304 | + ''' | ||
305 | + port_up | ||
306 | + ''' | ||
307 | + cmd = "linc:port_up({},{}).\r\n".format(self.lincId, port) | ||
308 | + self.write_to_cli(cmd) | ||
309 | + | ||
310 | + def w_port_down(self, port): | ||
311 | + ''' | ||
312 | + port_down | ||
313 | + ''' | ||
314 | + cmd = "linc:port_down({},{}).\r\n".format(self.lincId, port) | ||
315 | + self.write_to_cli(cmd) | ||
316 | + | ||
317 | + # helper functions | ||
318 | + @staticmethod | ||
319 | + def switchJSON(switch): | ||
320 | + "Returns the json configuration for a packet switch" | ||
321 | + configDict = {} | ||
322 | + configDict[ 'uri' ] = 'of:' + switch.dpid | ||
323 | + configDict[ 'mac' ] = quietRun('cat /sys/class/net/%s/address' % switch.name).strip('\n').translate(None, ':') | ||
324 | + configDict[ 'hw' ] = 'PK' # FIXME what about OVS? | ||
325 | + configDict[ 'mfr' ] = 'Linc' # FIXME what about OVS? | ||
326 | + configDict[ 'type' ] = 'SWITCH' # FIXME what about OVS? | ||
327 | + annotations = switch.params.get('annotations', {}) | ||
328 | + annotations.setdefault('name', switch.name) | ||
329 | + configDict[ 'annotations' ] = annotations | ||
330 | + ports = [] | ||
331 | + for port, intf in switch.intfs.items(): | ||
332 | + if intf.name == 'lo': | ||
333 | + continue | ||
334 | + portDict = {} | ||
335 | + portDict[ 'port' ] = port | ||
336 | + portDict[ 'type' ] = 'FIBER' if isinstance(intf.link, LINCLink) else 'COPPER' | ||
337 | + intfList = [ intf.link.intf1, intf.link.intf2 ] | ||
338 | + intfList.remove(intf) | ||
339 | + portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance(intf.link, LINCLink) else 0 | ||
340 | + ports.append(portDict) | ||
341 | + configDict[ 'ports' ] = ports | ||
342 | + return configDict | ||
343 | + | ||
344 | + @staticmethod | ||
345 | + def bootOE(net): | ||
346 | + "Start the LINC optical emulator within a mininet instance" | ||
347 | + opticalJSON = {} | ||
348 | + linkConfig = [] | ||
349 | + devices = [] | ||
350 | + #setting up the controllers for LINCSwitch class | ||
351 | + LINCSwitch.controllers = net.controllers | ||
352 | + | ||
353 | + for switch in net.switches: | ||
354 | + if isinstance(switch, OpticalSwitch): | ||
355 | + devices.append(switch.json()) | ||
356 | + else: | ||
357 | + devices.append(LINCSwitch.switchJSON(switch)) | ||
358 | + opticalJSON[ 'devices' ] = devices | ||
359 | + | ||
360 | + for link in net.links: | ||
361 | + if isinstance(link, LINCLink) : | ||
362 | + linkConfig.append(link.json()) | ||
363 | + opticalJSON[ 'links' ] = linkConfig | ||
364 | + | ||
365 | + info('*** Writing Topology.json file\n') | ||
366 | + with open('Topology.json', 'w') as outfile: | ||
367 | + json.dump(opticalJSON, outfile, indent=4, separators=(',', ': ')) | ||
368 | + | ||
369 | + info('*** Converting Topology.json to linc-oe format (TopoConfig.json) file\n') | ||
370 | + output = quietRun('%s/tools/test/bin/onos-oecfg ./Topology.json > TopoConfig.json' % LINCSwitch.onosDir, shell=True) | ||
371 | + if output: | ||
372 | + error('***ERROR: Error creating topology file: %s ' % output + '\n') | ||
373 | + return False | ||
374 | + | ||
375 | + info('*** Creating sys.config...\n') | ||
376 | + output = quietRun('%s/config_generator TopoConfig.json %s/sys.config.template %s %s' | ||
377 | + % (LINCSwitch.configGen, LINCSwitch.configGen, LINCSwitch.controllers[ 0 ].ip, LINCSwitch.controllers[ 0 ].port), shell=True) | ||
378 | + if output: | ||
379 | + error('***ERROR: Error creating sys.config file: %s\n' % output) | ||
380 | + return False | ||
381 | + | ||
382 | + info ('*** Setting multiple controllers in sys.config...\n') | ||
383 | + searchStr = '\[{"Switch.*$' | ||
384 | + ctrlStr = '' | ||
385 | + for index in range(len(LINCSwitch.controllers)): | ||
386 | + ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port) | ||
387 | + replaceStr = '[%s]},' % ctrlStr[:-1] # Cut off last comma | ||
388 | + sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr) | ||
389 | + output = quietRun(sedCmd, shell=True) | ||
390 | + | ||
391 | + info('*** Copying sys.config to linc-oe directory: ', output + '\n') | ||
392 | + output = quietRun('cp -v sys.config %s/rel/linc/releases/1.0/' % LINCSwitch.lincDir, shell=True).strip('\n') | ||
393 | + info(output + '\n') | ||
394 | + | ||
395 | + info('*** Adding taps and bringing them up...\n') | ||
396 | + LINCSwitch.setupInts(LINCSwitch.getTaps()) | ||
397 | + | ||
398 | + info('*** removing pipes if any \n') | ||
399 | + quietRun('rm /tmp/home/%s/linc-oe/rel/linc/*' % LINCSwitch.user, shell=True) | ||
400 | + | ||
401 | + info('*** Starting linc OE...\n') | ||
402 | + output = quietRun('%s/rel/linc/bin/linc start' % LINCSwitch.lincDir, shell=True) | ||
403 | + if output: | ||
404 | + error('***ERROR: LINC-OE: %s' % output + '\n') | ||
405 | + quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True) | ||
406 | + return False | ||
407 | + | ||
408 | + info('*** Waiting for linc-oe to start...\n') | ||
409 | + LINCSwitch.waitStarted(net) | ||
410 | + | ||
411 | + info('*** Adding cross-connect (tap) interfaces to packet switches...\n') | ||
412 | + for link in net.links: | ||
413 | + if isinstance(link, LINCLink): | ||
414 | + if link.annotations[ 'optical.type' ] == 'cross-connect': | ||
415 | + for intf in [ link.intf1, link.intf2 ]: | ||
416 | + if not isinstance(intf, LINCIntf): | ||
417 | + intfList = [ intf.link.intf1, intf.link.intf2 ] | ||
418 | + intfList.remove(intf) | ||
419 | + intf2 = intfList[ 0 ] | ||
420 | + intf.node.attach(LINCSwitch.findTap(intf2.node, intf2.node.ports[ intf2 ])) | ||
421 | + | ||
422 | + info('*** Waiting for all devices to be available in ONOS...\n') | ||
423 | + url = 'http://%s:8181/onos/v1/devices' % LINCSwitch.controllers[0].ip | ||
424 | + time = 0 | ||
425 | + while True: | ||
426 | + response = json.load(urllib2.urlopen(url)) | ||
427 | + devs = response.get('devices') | ||
428 | + | ||
429 | + # Wait for all devices to be registered | ||
430 | + if (len(devices) != len(devs)): | ||
431 | + continue | ||
96 | 432 | ||
433 | + # Wait for all devices to available | ||
434 | + available = True | ||
435 | + for d in devs: | ||
436 | + available &= d['available'] | ||
437 | + if available: | ||
438 | + break | ||
439 | + | ||
440 | + if (time >= TIMEOUT): | ||
441 | + error('***ERROR: ONOS did not register devices within %s seconds\n' % TIMEOUT) | ||
442 | + break | ||
443 | + | ||
444 | + time += SLEEP_TIME | ||
445 | + sleep(SLEEP_TIME) | ||
446 | + | ||
447 | + info('*** Pushing Topology.json to ONOS\n') | ||
448 | + for index in range(len(LINCSwitch.controllers)): | ||
449 | + output = quietRun('%s/tools/test/bin/onos-topo-cfg %s Topology.json &' % (LINCSwitch.onosDir, LINCSwitch.controllers[ index ].ip), shell=True) | ||
450 | + # successful output contains the two characters '{}' | ||
451 | + # if there is more output than this, there is an issue | ||
452 | + if output.strip('{}'): | ||
453 | + warn('***WARNING: Could not push topology file to ONOS: %s\n' % output) | ||
454 | + | ||
455 | + @staticmethod | ||
456 | + def waitStarted(net, timeout=TIMEOUT): | ||
457 | + "wait until all tap interfaces are available" | ||
458 | + tapCount = 0 | ||
459 | + time = 0 | ||
460 | + for link in net.links: | ||
461 | + if isinstance(link, LINCLink): | ||
462 | + if link.annotations[ 'optical.type' ] == 'cross-connect': | ||
463 | + tapCount += 1 | ||
464 | + | ||
465 | + while True: | ||
466 | + if str(tapCount) == quietRun('ip addr | grep tap | wc -l', shell=True).strip('\n'): | ||
467 | + return True | ||
468 | + if timeout: | ||
469 | + if time >= TIMEOUT: | ||
470 | + error('***ERROR: LINC OE did not start within %s seconds\n' % TIMEOUT) | ||
471 | + return False | ||
472 | + time += SLEEP_TIME | ||
473 | + sleep(SLEEP_TIME) | ||
474 | + | ||
475 | + @staticmethod | ||
476 | + def shutdownOE(): | ||
477 | + "stop the optical emulator" | ||
478 | + info('*** Stopping linc OE...\n') | ||
479 | + quietRun('%s/rel/linc/bin/linc stop' % LINCSwitch.lincDir, shell=True) | ||
480 | + | ||
481 | + @staticmethod | ||
482 | + def setupInts(intfs): | ||
483 | + ''' | ||
484 | + add taps and bring them up. | ||
485 | + ''' | ||
486 | + for i in intfs: | ||
487 | + quietRun('ip tuntap add dev %s mode tap' % i) | ||
488 | + quietRun('ip link set dev %s up' % i) | ||
489 | + info('*** Intf %s set\n' % i) | ||
490 | + | ||
491 | + @staticmethod | ||
492 | + def getTaps(path=None): | ||
493 | + ''' | ||
494 | + return list of all the tops in sys.config | ||
495 | + ''' | ||
496 | + if path is None: | ||
497 | + path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir | ||
498 | + fd = open(path, 'r', 0) | ||
499 | + sys_data = fd.read() | ||
500 | + taps = re.findall('tap\d+', sys_data) | ||
501 | + fd.close() | ||
502 | + return taps | ||
503 | + | ||
504 | + @staticmethod | ||
505 | + def findUser(): | ||
506 | + "Try to return logged-in (usually non-root) user" | ||
507 | + try: | ||
508 | + # If we're running sudo | ||
509 | + return os.environ[ 'SUDO_USER' ] | ||
510 | + except: | ||
511 | + try: | ||
512 | + # Logged-in user (if we have a tty) | ||
513 | + return quietRun('who am i').split()[ 0 ] | ||
514 | + except: | ||
515 | + # Give up and return effective user | ||
516 | + return quietRun('whoami') | ||
517 | + | ||
518 | + | ||
519 | + @staticmethod | ||
520 | + def findTap(node, port, path=None): | ||
521 | + '''utility function to parse through a sys.config | ||
522 | + file to find tap interfaces for a switch''' | ||
523 | + switch = False | ||
524 | + portLine = '' | ||
525 | + intfLines = [] | ||
526 | + | ||
527 | + if path is None: | ||
528 | + path = '%s/rel/linc/releases/1.0/sys.config' % LINCSwitch.lincDir | ||
529 | + | ||
530 | + with open(path) as f: | ||
531 | + for line in f: | ||
532 | + if 'tap' in line: | ||
533 | + intfLines.append(line) | ||
534 | + if node.dpid in line.translate(None, ':'): | ||
535 | + switch = True | ||
536 | + continue | ||
537 | + if switch: | ||
538 | + if 'switch' in line: | ||
539 | + switch = False | ||
540 | + if 'port_no,%s}' % port in line: | ||
541 | + portLine = line | ||
542 | + break | ||
543 | + | ||
544 | + if portLine: | ||
545 | + m = re.search('port,\d+', portLine) | ||
546 | + port = m.group(0).split(',')[ 1 ] | ||
547 | + else: | ||
548 | + error('***ERROR: Could not find any ports in sys.config\n') | ||
549 | + return | ||
97 | 550 | ||
98 | - def json( self ): | 551 | + for intfLine in intfLines: |
552 | + if 'port,%s' % port in intfLine: | ||
553 | + return re.findall('tap\d+', intfLine)[ 0 ] | ||
554 | + | ||
555 | + def json(self): | ||
99 | "return json configuration dictionary for switch" | 556 | "return json configuration dictionary for switch" |
100 | return self.configDict | 557 | return self.configDict |
101 | - | ||
102 | - def terminate( self ): | ||
103 | - pass | ||
104 | 558 | ||
105 | -class OpticalLink( Link ): | 559 | + def terminate(self): |
560 | + pass | ||
106 | 561 | ||
107 | - def __init__( self, node1, node2, port1=None, port2=None, allowed=True, | 562 | +class LINCLink(Link): |
563 | + """ | ||
564 | + LINC link class | ||
565 | + """ | ||
566 | + def __init__(self, node1, node2, port1=None, port2=None, allowed=True, | ||
108 | intfName1=None, intfName2=None, linkType='OPTICAL', | 567 | intfName1=None, intfName2=None, linkType='OPTICAL', |
109 | - annotations={}, speed1=0, speed2=0, **params ): | 568 | + annotations={}, speed1=0, speed2=0, **params): |
110 | "Creates a dummy link without a virtual ethernet pair." | 569 | "Creates a dummy link without a virtual ethernet pair." |
111 | self.allowed = allowed | 570 | self.allowed = allowed |
112 | self.annotations = annotations | 571 | self.annotations = annotations |
113 | self.linkType = linkType | 572 | self.linkType = linkType |
573 | + self.port1 = port1 | ||
574 | + self.port2 = port2 | ||
114 | params1 = { 'speed': speed1 } | 575 | params1 = { 'speed': speed1 } |
115 | params2 = { 'speed': speed2 } | 576 | params2 = { 'speed': speed2 } |
116 | - | 577 | + # self.isCrossConnect = True if self.annotations.get('optical.type') == 'cross-connect' else False |
117 | - if isinstance( node1, OpticalSwitch ): | 578 | + if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch): |
118 | - cls1 = OpticalIntf | 579 | + self.isCrossConnect = False |
580 | + else: | ||
581 | + self.isCrossConnect = True | ||
582 | + if isinstance(node1, LINCSwitch): | ||
583 | + cls1 = LINCIntf | ||
584 | + if self.isCrossConnect: | ||
585 | + node1.crossConnects.append(self) | ||
119 | else: | 586 | else: |
120 | cls1 = Intf | 587 | cls1 = Intf |
121 | # bad hack to stop error message from appearing when we try to set up intf in a packet switch, | 588 | # bad hack to stop error message from appearing when we try to set up intf in a packet switch, |
122 | # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up | 589 | # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up |
123 | intfName1 = 'lo' | 590 | intfName1 = 'lo' |
124 | - if isinstance( node2, OpticalSwitch ): | 591 | + if isinstance(node2, LINCSwitch): |
125 | - cls2 = OpticalIntf | 592 | + cls2 = LINCIntf |
593 | + if self.isCrossConnect: | ||
594 | + node2.crossConnects.append(self) | ||
126 | else: | 595 | else: |
127 | cls2 = Intf | 596 | cls2 = Intf |
128 | intfName2 = 'lo' | 597 | intfName2 = 'lo' |
129 | - Link.__init__( self, node1, node2, port1=port1, port2=port2, | 598 | + Link.__init__(self, node1, node2, port1=port1, port2=port2, |
130 | intfName1=intfName1, intfName2=intfName2, cls1=cls1, | 599 | intfName1=intfName1, intfName2=intfName2, cls1=cls1, |
131 | - cls2=cls2, params1=params1, params2=params2 ) | 600 | + cls2=cls2, params1=params1, params2=params2) |
132 | - | ||
133 | 601 | ||
134 | @classmethod | 602 | @classmethod |
135 | - def makeIntfPair( _cls, intfName1, intfName2, *args, **kwargs ): | 603 | + def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs): |
136 | pass | 604 | pass |
137 | 605 | ||
138 | - def json( self ): | 606 | + def json(self): |
139 | "build and return the json configuration dictionary for this link" | 607 | "build and return the json configuration dictionary for this link" |
140 | configData = {} | 608 | configData = {} |
141 | - configData[ 'src' ] = ( 'of:' + self.intf1.node.dpid + | 609 | + configData[ 'src' ] = ('of:' + self.intf1.node.dpid + |
142 | - '/%s' % self.intf1.node.ports[ self.intf1 ] ) | 610 | + '/%s' % self.intf1.node.ports[ self.intf1 ]) |
143 | - configData[ 'dst' ] = ( 'of:' + self.intf2.node.dpid + | 611 | + configData[ 'dst' ] = ('of:' + self.intf2.node.dpid + |
144 | - '/%s' % self.intf2.node.ports[ self.intf2 ] ) | 612 | + '/%s' % self.intf2.node.ports[ self.intf2 ]) |
145 | configData[ 'type' ] = self.linkType | 613 | configData[ 'type' ] = self.linkType |
146 | configData[ 'annotations' ] = self.annotations | 614 | configData[ 'annotations' ] = self.annotations |
147 | return configData | 615 | return configData |
148 | 616 | ||
149 | -class OpticalIntf( Intf ): | 617 | +class LINCIntf(OpticalIntf): |
150 | - | 618 | + """ |
151 | - def __init__( self, name=None, node=None, speed=0, | 619 | + LINC interface class |
152 | - port=None, link=None, **params ): | 620 | + """ |
621 | + def __init__(self, name=None, node=None, speed=0, | ||
622 | + port=None, link=None, **params): | ||
153 | self.node = node | 623 | self.node = node |
154 | self.speed = speed | 624 | self.speed = speed |
155 | self.port = port | 625 | self.port = port |
156 | self.link = link | 626 | self.link = link |
157 | self.name = name | 627 | self.name = name |
158 | - node.addIntf( self, port=port ) | 628 | + node.addIntf(self, port=port) |
159 | self.params = params | 629 | self.params = params |
160 | self.ip = None | 630 | self.ip = None |
161 | 631 | ||
162 | - def json( self ): | 632 | + def json(self): |
163 | "build and return the JSON information for this interface( not used right now )" | 633 | "build and return the JSON information for this interface( not used right now )" |
164 | configDict = {} | 634 | configDict = {} |
165 | configDict[ 'port' ] = self.port | 635 | configDict[ 'port' ] = self.port |
... | @@ -167,356 +637,34 @@ class OpticalIntf( Intf ): | ... | @@ -167,356 +637,34 @@ class OpticalIntf( Intf ): |
167 | configDict[ 'type' ] = 'FIBER' | 637 | configDict[ 'type' ] = 'FIBER' |
168 | return configDict | 638 | return configDict |
169 | 639 | ||
170 | - def config( self, *args, **kwargs ): | 640 | + def config(self, *args, **kwargs): |
171 | "dont configure a dummy interface" | 641 | "dont configure a dummy interface" |
172 | pass | 642 | pass |
173 | 643 | ||
174 | -def switchJSON( switch ): | 644 | + def ifconfig(self, status): |
175 | - "Returns the json configuration for a packet switch" | 645 | + "configure the status" |
176 | - configDict = {} | 646 | + if status == "up": |
177 | - configDict[ 'uri' ] = 'of:' + switch.dpid | 647 | + return self.node.w_port_up(self.port) |
178 | - configDict[ 'mac' ] = quietRun( 'cat /sys/class/net/%s/address' % switch.name ).strip( '\n' ).translate( None, ':' ) | 648 | + elif status == "down": |
179 | - configDict[ 'hw' ] = 'PK' # FIXME what about OVS? | 649 | + return self.node.w_port_down(self.port) |
180 | - configDict[ 'mfr' ] = 'Linc' # FIXME what about OVS? | ||
181 | - configDict[ 'type' ] = 'SWITCH' # FIXME what about OVS? | ||
182 | - annotations = switch.params.get( 'annotations', {} ) | ||
183 | - annotations.setdefault( 'name', switch.name ) | ||
184 | - configDict[ 'annotations' ] = annotations | ||
185 | - ports = [] | ||
186 | - for port, intf in switch.intfs.items(): | ||
187 | - if intf.name == 'lo': | ||
188 | - continue | ||
189 | - portDict = {} | ||
190 | - portDict[ 'port' ] = port | ||
191 | - portDict[ 'type' ] = 'FIBER' if isinstance( intf.link, OpticalLink ) else 'COPPER' | ||
192 | - intfList = [ intf.link.intf1, intf.link.intf2 ] | ||
193 | - intfList.remove( intf ) | ||
194 | - portDict[ 'speed' ] = intfList[ 0 ].speed if isinstance( intf.link, OpticalLink ) else 0 | ||
195 | - ports.append( portDict ) | ||
196 | - configDict[ 'ports' ] = ports | ||
197 | - return configDict | ||
198 | - | ||
199 | - | ||
200 | -def startOE( net ): | ||
201 | - "Start the LINC optical emulator within a mininet instance" | ||
202 | - opticalJSON = {} | ||
203 | - linkConfig = [] | ||
204 | - devices = [] | ||
205 | - | ||
206 | - for switch in net.switches: | ||
207 | - if isinstance( switch, OpticalSwitch ): | ||
208 | - devices.append( switch.json() ) | ||
209 | - else: | ||
210 | - devices.append( switchJSON( switch ) ) | ||
211 | - opticalJSON[ 'devices' ] = devices | ||
212 | - | ||
213 | - for link in net.links: | ||
214 | - if isinstance( link, OpticalLink ) : | ||
215 | - linkConfig.append( link.json() ) | ||
216 | - | ||
217 | - opticalJSON[ 'links' ] = linkConfig | ||
218 | - | ||
219 | - try: | ||
220 | - onosDir = os.environ[ 'ONOS_ROOT' ] | ||
221 | - except: | ||
222 | - onosDir = findDir( 'onos' ) | ||
223 | - if not onosDir: | ||
224 | - error( 'Please set ONOS_ROOT environment variable!\n' ) | ||
225 | - return False | ||
226 | - else: | ||
227 | - os.environ[ 'ONOS_ROOT' ] = onosDir | ||
228 | - | ||
229 | - info( '*** Writing Topology.json file\n' ) | ||
230 | - with open( 'Topology.json', 'w' ) as outfile: | ||
231 | - json.dump( opticalJSON, outfile, indent=4, separators=(',', ': ') ) | ||
232 | - | ||
233 | - info( '*** Converting Topology.json to linc-oe format (TopoConfig.json) file (not using oecfg) \n' ) | ||
234 | - | ||
235 | - topoConfigJson = {}; | ||
236 | - newLinkConfig = []; | ||
237 | - switchConfig = []; | ||
238 | - dpIdToName = {}; | ||
239 | - | ||
240 | - #Iterate through all switches and convert the ROADM switches to linc-oe format | ||
241 | - for switch in opticalJSON["devices"]: | ||
242 | - if switch["type"] == "ROADM": | ||
243 | - builtSwitch = {} | ||
244 | - | ||
245 | - #set basic switch params based on annotations | ||
246 | - builtSwitch["allowed"] = True; | ||
247 | - builtSwitch["latitude"] = switch["annotations"]["latitude"]; | ||
248 | - builtSwitch["longitude"] = switch["annotations"]["longitude"]; | ||
249 | - | ||
250 | - nodeId = switch["uri"] | ||
251 | - | ||
252 | - #convert the nodeId to linc-oe format | ||
253 | - nodeDpid = dpId(nodeId); | ||
254 | - | ||
255 | - if "name" in switch: | ||
256 | - builtSwitch["name"] = switch["name"] | ||
257 | - else: | ||
258 | - builtSwitch["name"] = "none" | ||
259 | - | ||
260 | - #keep track of the name corresponding to each switch dpid | ||
261 | - dpIdToName[nodeDpid] = builtSwitch["name"]; | ||
262 | - | ||
263 | - builtSwitch["nodeDpid"] = nodeDpid | ||
264 | - | ||
265 | - #set switch params and type | ||
266 | - builtSwitch["params"] = {}; | ||
267 | - builtSwitch["params"]["numregens"] = switch["annotations"]["optical.regens"]; | ||
268 | - builtSwitch["type"] = "Roadm" | ||
269 | 650 | ||
270 | - #append to list of switches | ||
271 | - switchConfig.append(builtSwitch); | ||
272 | 651 | ||
273 | - topoConfigJson["switchConfig"] = switchConfig; | 652 | +class MininetOE(Mininet): |
274 | - | ||
275 | - #Iterate through all optical links and convert them to linc-oe format | ||
276 | - for link in opticalJSON["links"]: | ||
277 | - if link["type"] == "OPTICAL": | ||
278 | - builtLink = {} | ||
279 | - | ||
280 | - #set basic link params for src and dst | ||
281 | - builtLink["allowed"] = True; | ||
282 | - builtLink["nodeDpid1"] = dpId(link["src"]) | ||
283 | - builtLink["nodeDpid2"] = dpId(link["dst"]) | ||
284 | - | ||
285 | - #set more params such as name/bandwidth/port/waves if they exist | ||
286 | - params = {} | ||
287 | - params["nodeName1"] = dpIdToName.get(builtLink["nodeDpid1"], "none") | ||
288 | - params["nodeName2"] = dpIdToName.get(builtLink["nodeDpid2"], "none") | ||
289 | - if "bandwidth" in link["annotations"]: | ||
290 | - params["bandwidth"] = link["annotations"]["bandwidth"] | ||
291 | - params["port1"] = int(link["src"].split("/")[1]) | ||
292 | - params["port2"] = int(link["dst"].split("/")[1]) | ||
293 | - | ||
294 | - if "optical.waves" in link["annotations"]: | ||
295 | - params["numWaves"] = link["annotations"]["optical.waves"] | ||
296 | - builtLink["params"] = params | ||
297 | - | ||
298 | - #set type of link (WDM or pktOpt) | ||
299 | - if link["annotations"]["optical.type"] == "WDM": | ||
300 | - builtLink["type"] = "wdmLink" | ||
301 | - else: | ||
302 | - builtLink["type"] = "pktOptLink" | ||
303 | - | ||
304 | - newLinkConfig.append(builtLink); | ||
305 | - | ||
306 | - topoConfigJson["linkConfig"] = newLinkConfig; | ||
307 | - | ||
308 | - #Writing to TopoConfig.json | ||
309 | - with open( 'TopoConfig.json', 'w' ) as outfile: | ||
310 | - json.dump( topoConfigJson, outfile, indent=4, separators=(',', ': ') ) | ||
311 | - | ||
312 | - info( '*** Creating sys.config...\n' ) | ||
313 | - configGen = findDir( 'LINC-config-generator' ) | ||
314 | - if not configGen: | ||
315 | - error( "***ERROR: Could not find LINC-config-generator in user's home directory\n" ) | ||
316 | - return False | ||
317 | - output = quietRun( '%s/config_generator TopoConfig.json %s/sys.config.template %s %s' | ||
318 | - % ( configGen, configGen, net.controllers[ 0 ].ip, net.controllers[ 0 ].port ), shell=True ) | ||
319 | - if output: | ||
320 | - error( '***ERROR: Error creating sys.config file: %s\n' % output ) | ||
321 | - return False | ||
322 | - | ||
323 | - info ('*** Setting multiple controllers in sys.config...\n' ) | ||
324 | - searchStr = '{controllers,.*$' | ||
325 | - ctrlStr = '' | ||
326 | - for index in range(len(net.controllers)): | ||
327 | - ctrlStr += '{"Switch%d-Controller","%s",%d,tcp},' % (index, net.controllers[index].ip, net.controllers[index].port) | ||
328 | - replaceStr = '{controllers,[%s]},' % ctrlStr[:-1] # Cut off last comma | ||
329 | - sedCmd = 'sed -i \'s/%s/%s/\' sys.config' % (searchStr, replaceStr) | ||
330 | - output = quietRun( sedCmd, shell=True ) | ||
331 | - | ||
332 | - info( '*** Copying sys.config to linc-oe directory: ', output + '\n' ) | ||
333 | - lincDir = findDir( 'linc-oe' ) | ||
334 | - if not lincDir: | ||
335 | - error( "***ERROR: Could not find linc-oe in user's home directory\n" ) | ||
336 | - return False | ||
337 | - output = quietRun( 'cp -v sys.config %s/rel/linc/releases/1.0/' % lincDir, shell=True ).strip( '\n' ) | ||
338 | - info( output + '\n' ) | ||
339 | - | ||
340 | - info( '*** Starting linc OE...\n' ) | ||
341 | - output = quietRun( '%s/rel/linc/bin/linc start' % lincDir, shell=True ) | ||
342 | - if output: | ||
343 | - error( '***ERROR: LINC-OE: %s' % output + '\n' ) | ||
344 | - quietRun( '%s/rel/linc/bin/linc stop' % lincDir, shell=True ) | ||
345 | - return False | ||
346 | - | ||
347 | - info( '*** Waiting for linc-oe to start...\n' ) | ||
348 | - waitStarted( net ) | ||
349 | - | ||
350 | - info( '*** Adding cross-connect (tap) interfaces to packet switches...\n' ) | ||
351 | - for link in net.links: | ||
352 | - if isinstance( link, OpticalLink ): | ||
353 | - if link.annotations[ 'optical.type' ] == 'cross-connect': | ||
354 | - for intf in [ link.intf1, link.intf2 ]: | ||
355 | - if not isinstance( intf, OpticalIntf ): | ||
356 | - intfList = [ intf.link.intf1, intf.link.intf2 ] | ||
357 | - intfList.remove( intf ) | ||
358 | - intf2 = intfList[ 0 ] | ||
359 | - intf.node.attach( findTap( intf2.node, intf2.node.ports[ intf2 ] ) ) | ||
360 | - | ||
361 | - info( '*** Press ENTER to push Topology.json to onos...\n' ) | ||
362 | - raw_input() # FIXME... we should eventually remove this | ||
363 | - info( '*** Pushing Topology.json to ONOS\n' ) | ||
364 | - output = quietRun( '%s/tools/test/bin/onos-topo-cfg %s Topology.json' % ( onosDir, net.controllers[ 0 ].ip ), shell=True ) | ||
365 | - # successful output contains the two characters '{}' | ||
366 | - # if there is more output than this, there is an issue | ||
367 | - if output.strip( '{}' ): | ||
368 | - warn( '***WARNING: Could not push topology file to ONOS: %s' % output ) | ||
369 | - | ||
370 | -#converts node ids to linc-oe format, with colons every two chars | ||
371 | -def dpId(id): | ||
372 | - nodeDpid = "" | ||
373 | - id = id.split("/", 1)[0] | ||
374 | - for i in range(3, len(id) - 1, 2): | ||
375 | - nodeDpid += (id[i:(i + 2):]) + ":" | ||
376 | - return nodeDpid[0:(len(nodeDpid) - 1)]; | ||
377 | - | ||
378 | -def waitStarted( net, timeout=None ): | ||
379 | - "wait until all tap interfaces are available" | ||
380 | - tapCount = 0 | ||
381 | - time = 0 | ||
382 | - for link in net.links: | ||
383 | - if isinstance( link, OpticalLink ): | ||
384 | - if link.annotations[ 'optical.type' ] == 'cross-connect': | ||
385 | - tapCount += 1 | ||
386 | - | ||
387 | - while True: | ||
388 | - if str( tapCount ) == quietRun( 'ip addr | grep tap | wc -l', shell=True ).strip( '\n' ): | ||
389 | - return True | ||
390 | - if timeout: | ||
391 | - if time >= timeout: | ||
392 | - error( '***ERROR: Linc OE did not start within %s seconds' % timeout ) | ||
393 | - return False | ||
394 | - time += .5 | ||
395 | - sleep( .5 ) | ||
396 | - | ||
397 | -def stopOE(): | ||
398 | - "stop the optical emulator" | ||
399 | - info( '*** Stopping linc OE...\n' ) | ||
400 | - lincDir = findDir( 'linc-oe' ) | ||
401 | - quietRun( '%s/rel/linc/bin/linc stop' % lincDir, shell=True ) | ||
402 | - | ||
403 | -def findDir( directory ): | ||
404 | - "finds and returns the path of any directory in the user's home directory" | ||
405 | - user = findUser() | ||
406 | - homeDir = '/home/' + user | ||
407 | - Dir = quietRun( 'find %s -maxdepth 1 -name %s -type d' % ( homeDir, directory ) ).strip( '\n' ) | ||
408 | - DirList = Dir.split( '\n' ) | ||
409 | - if not Dir: | ||
410 | - return None | ||
411 | - elif len( DirList ) > 1 : | ||
412 | - warn( '***WARNING: Found multiple instances of %s; using %s\n' | ||
413 | - % ( directory, DirList[ 0 ] ) ) | ||
414 | - return DirList[ 0 ] | ||
415 | - else: | ||
416 | - return Dir | ||
417 | - | ||
418 | -def findUser(): | ||
419 | - "Try to return logged-in (usually non-root) user" | ||
420 | - try: | ||
421 | - # If we're running sudo | ||
422 | - return os.environ[ 'SUDO_USER' ] | ||
423 | - except: | ||
424 | - try: | ||
425 | - # Logged-in user (if we have a tty) | ||
426 | - return quietRun( 'who am i' ).split()[ 0 ] | ||
427 | - except: | ||
428 | - # Give up and return effective user | ||
429 | - return quietRun( 'whoami' ) | ||
430 | - | ||
431 | - | ||
432 | -def findTap( node, port, path=None ): | ||
433 | - '''utility function to parse through a sys.config | ||
434 | - file to find tap interfaces for a switch''' | ||
435 | - switch=False | ||
436 | - portLine = '' | ||
437 | - intfLines = [] | ||
438 | - | ||
439 | - if path is None: | ||
440 | - lincDir = findDir( 'linc-oe' ) | ||
441 | - if not lincDir: | ||
442 | - error( '***ERROR: Could not find linc-oe in users home directory\n' ) | ||
443 | - return None | ||
444 | - path = '%s/rel/linc/releases/1.0/sys.config' % lincDir | ||
445 | - | ||
446 | - with open( path ) as f: | ||
447 | - for line in f: | ||
448 | - if 'tap' in line: | ||
449 | - intfLines.append( line ) | ||
450 | - if node.dpid in line.translate( None, ':' ): | ||
451 | - switch=True | ||
452 | - continue | ||
453 | - if switch: | ||
454 | - if 'switch' in line: | ||
455 | - switch = False | ||
456 | - if 'port_no,%s}' % port in line: | ||
457 | - portLine = line | ||
458 | - break | ||
459 | - | ||
460 | - if portLine: | ||
461 | - m = re.search( 'port,\d+', portLine ) | ||
462 | - port = m.group( 0 ).split( ',' )[ 1 ] | ||
463 | - else: | ||
464 | - error( '***ERROR: Could not find any ports in sys.config\n' ) | ||
465 | - return | ||
466 | - | ||
467 | - for intfLine in intfLines: | ||
468 | - if 'port,%s' % port in intfLine: | ||
469 | - return re.findall( 'tap\d+', intfLine )[ 0 ] | ||
470 | - | ||
471 | - | ||
472 | -class MininetOE( Mininet ): | ||
473 | "Mininet with Linc-OE support (starts and stops linc-oe)" | 653 | "Mininet with Linc-OE support (starts and stops linc-oe)" |
474 | 654 | ||
475 | - def start( self ): | 655 | + def start(self): |
476 | - Mininet.start( self ) | 656 | + Mininet.start(self) |
477 | - startOE( self ) | 657 | + LINCSwitch.bootOE(self) |
478 | 658 | ||
479 | - def stop( self ): | 659 | + def stop(self): |
480 | - Mininet.stop( self ) | 660 | + Mininet.stop(self) |
481 | - stopOE() | 661 | + LINCSwitch.shutdownOE() |
482 | 662 | ||
483 | - def addControllers( self, controllers ): | 663 | + def addControllers(self, controllers): |
484 | i = 0 | 664 | i = 0 |
485 | for ctrl in controllers: | 665 | for ctrl in controllers: |
486 | - self.addController( RemoteController( 'c%d' % i, ip=ctrl ) ) | 666 | + self.addController(RemoteController('c%d' % i, ip=ctrl)) |
487 | - | 667 | + i += 1 |
488 | - | ||
489 | -class OpticalTestTopo( Topo ): | ||
490 | - | ||
491 | - def build( self ): | ||
492 | - opticalAnn = { 'optical.waves': 80, 'optical.type': "WDM", 'durable': True } | ||
493 | - switchAnn = { 'bandwidth': 100000, 'optical.type': 'cross-connect', 'durable': True } | ||
494 | - h1 = self.addHost( 'h1' ) | ||
495 | - h2 = self.addHost( 'h2' ) | ||
496 | - s1 = self.addSwitch( 's1' ) | ||
497 | - s2 = self.addSwitch( 's2' ) | ||
498 | - O4 = self.addSwitch( 'O4', cls=OpticalSwitch ) | ||
499 | - O5 = self.addSwitch( 'O5', cls=OpticalSwitch ) | ||
500 | - O6 = self.addSwitch( 'O6', cls=OpticalSwitch ) | ||
501 | - self.addLink( O4, O5, cls=OpticalLink, annotations=opticalAnn ) | ||
502 | - self.addLink( O5, O6, cls=OpticalLink, annotations=opticalAnn ) | ||
503 | - self.addLink( s1, O4, cls=OpticalLink, annotations=switchAnn ) | ||
504 | - self.addLink( s2, O6, cls=OpticalLink, annotations=switchAnn ) | ||
505 | - self.addLink( h1, s1 ) | ||
506 | - self.addLink( h2, s2 ) | ||
507 | 668 | ||
508 | if __name__ == '__main__': | 669 | if __name__ == '__main__': |
509 | - import sys | 670 | + pass |
510 | - if len( sys.argv ) >= 2: | ||
511 | - controllers = sys.argv[1:] | ||
512 | - else: | ||
513 | - print 'Usage: ./opticalUtils.py (<Controller IP>)+' | ||
514 | - print 'Using localhost...\n' | ||
515 | - controllers = [ '127.0.0.1' ] | ||
516 | - | ||
517 | - setLogLevel( 'info' ) | ||
518 | - net = MininetOE( topo=OpticalTestTopo(), controller=None, autoSetMacs=True ) | ||
519 | - net.addControllers( controllers ) | ||
520 | - net.start() | ||
521 | - CLI( net ) | ||
522 | - net.stop() | ... | ... |
-
Please register or login to post a comment