Committed by
Gerrit Code Review
OECF removed working
Change-Id: I178a227fedacb4efc8669c839042d952f4efd670
Showing
4 changed files
with
382 additions
and
746 deletions
apps/oecfg/pom.xml
deleted
100644 → 0
| 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 | -} |
| ... | @@ -40,7 +40,6 @@ | ... | @@ -40,7 +40,6 @@ |
| 40 | <module>sdnip</module> | 40 | <module>sdnip</module> |
| 41 | <module>optical</module> | 41 | <module>optical</module> |
| 42 | <module>metrics</module> | 42 | <module>metrics</module> |
| 43 | - <module>oecfg</module> | ||
| 44 | <module>routing</module> | 43 | <module>routing</module> |
| 45 | <module>routing-api</module> | 44 | <module>routing-api</module> |
| 46 | <module>reactive-routing</module> | 45 | <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, LINCLink, and OpticalSwitch | 7 | +such as startOE, stopOE, OpticalLink, 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, LINCLink, and OpticalSwitch | ... | @@ -22,10 +22,10 @@ such as startOE, stopOE, LINCLink, and OpticalSwitch |
| 22 | 22 | ||
| 23 | Usage: | 23 | Usage: |
| 24 | ------------ | 24 | ------------ |
| 25 | - - import LINCLink and OpticalSwitch from this module | 25 | + - import OpticalLink 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=LINCLink | 28 | + to an optical switch with topo.addLink, always specify cls=OpticalLink |
| 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,12 +51,11 @@ switches have been started, the new Mininet start() method should also push the | ... | @@ -51,12 +51,11 @@ 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 | -import sys | 54 | + |
| 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 | ||
| 60 | 59 | ||
| 61 | from mininet.node import Switch, RemoteController | 60 | from mininet.node import Switch, RemoteController |
| 62 | from mininet.topo import Topo | 61 | from mininet.topo import Topo |
| ... | @@ -66,127 +65,25 @@ from mininet.log import setLogLevel, info, error, warn | ... | @@ -66,127 +65,25 @@ from mininet.log import setLogLevel, info, error, warn |
| 66 | from mininet.link import Link, Intf | 65 | from mininet.link import Link, Intf |
| 67 | from mininet.cli import CLI | 66 | from mininet.cli import CLI |
| 68 | 67 | ||
| 69 | -# Sleep time and timeout values in seconds | 68 | +class OpticalSwitch( Switch ): |
| 70 | -SLEEP_TIME = 2 | 69 | + |
| 71 | -TIMEOUT = 60 | 70 | + def __init__( self, name, dpid=None, allowed=True, |
| 72 | - | 71 | + switchType='ROADM', annotations={}, **params ): |
| 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): | ||
| 164 | params[ 'inNamespace' ] = False | 72 | params[ 'inNamespace' ] = False |
| 165 | - Switch.__init__(self, name, dpid=dpid, **params) | 73 | + Switch.__init__( self, name, dpid=dpid, **params ) |
| 166 | self.name = name | 74 | self.name = name |
| 167 | self.annotations = annotations | 75 | self.annotations = annotations |
| 168 | self.allowed = allowed | 76 | self.allowed = allowed |
| 169 | self.switchType = switchType | 77 | self.switchType = switchType |
| 170 | - self.configDict = {} # dictionary that holds all of the JSON configuration data | 78 | + self.configDict = {} # dictionary that holds all of the JSON configuration data |
| 171 | - self.crossConnects = [] | 79 | + |
| 172 | - self.deletedCrossConnects = [] | 80 | + def start( self, *opts, **params ): |
| 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): | ||
| 178 | '''Instead of starting a virtual switch, we build the JSON | 81 | '''Instead of starting a virtual switch, we build the JSON |
| 179 | dictionary for the emulated optical switch''' | 82 | 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() | ||
| 186 | self.configDict[ 'uri' ] = 'of:' + self.dpid | 83 | self.configDict[ 'uri' ] = 'of:' + self.dpid |
| 187 | self.configDict[ 'annotations' ] = self.annotations | 84 | self.configDict[ 'annotations' ] = self.annotations |
| 188 | - self.configDict[ 'annotations' ].setdefault('name', self.name) | 85 | + self.configDict[ 'annotations' ].setdefault( 'name', self.name ) |
| 189 | - self.configDict[ 'hw' ] = 'LINC-OE' | 86 | + self.configDict[ 'hw' ] = 'OE' |
| 190 | self.configDict[ 'mfr' ] = 'Linc' | 87 | self.configDict[ 'mfr' ] = 'Linc' |
| 191 | self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1] | 88 | self.configDict[ 'mac' ] = 'ffffffffffff' + self.dpid[-2] + self.dpid[-1] |
| 192 | self.configDict[ 'type' ] = self.switchType | 89 | self.configDict[ 'type' ] = self.switchType |
| ... | @@ -195,441 +92,74 @@ class LINCSwitch(OpticalSwitch): | ... | @@ -195,441 +92,74 @@ class LINCSwitch(OpticalSwitch): |
| 195 | if intf.name == 'lo': | 92 | if intf.name == 'lo': |
| 196 | continue | 93 | continue |
| 197 | else: | 94 | else: |
| 198 | - self.configDict[ 'ports' ].append(intf.json()) | 95 | + 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 | ||
| 432 | 96 | ||
| 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 | ||
| 550 | 97 | ||
| 551 | - for intfLine in intfLines: | 98 | + def json( self ): |
| 552 | - if 'port,%s' % port in intfLine: | ||
| 553 | - return re.findall('tap\d+', intfLine)[ 0 ] | ||
| 554 | - | ||
| 555 | - def json(self): | ||
| 556 | "return json configuration dictionary for switch" | 99 | "return json configuration dictionary for switch" |
| 557 | return self.configDict | 100 | return self.configDict |
| 558 | - | 101 | + |
| 559 | - def terminate(self): | 102 | + def terminate( self ): |
| 560 | pass | 103 | pass |
| 561 | 104 | ||
| 562 | -class LINCLink(Link): | 105 | +class OpticalLink( Link ): |
| 563 | - """ | 106 | + |
| 564 | - LINC link class | 107 | + def __init__( self, node1, node2, port1=None, port2=None, allowed=True, |
| 565 | - """ | ||
| 566 | - def __init__(self, node1, node2, port1=None, port2=None, allowed=True, | ||
| 567 | intfName1=None, intfName2=None, linkType='OPTICAL', | 108 | intfName1=None, intfName2=None, linkType='OPTICAL', |
| 568 | - annotations={}, speed1=0, speed2=0, **params): | 109 | + annotations={}, speed1=0, speed2=0, **params ): |
| 569 | "Creates a dummy link without a virtual ethernet pair." | 110 | "Creates a dummy link without a virtual ethernet pair." |
| 570 | self.allowed = allowed | 111 | self.allowed = allowed |
| 571 | self.annotations = annotations | 112 | self.annotations = annotations |
| 572 | self.linkType = linkType | 113 | self.linkType = linkType |
| 573 | - self.port1 = port1 | ||
| 574 | - self.port2 = port2 | ||
| 575 | params1 = { 'speed': speed1 } | 114 | params1 = { 'speed': speed1 } |
| 576 | params2 = { 'speed': speed2 } | 115 | params2 = { 'speed': speed2 } |
| 577 | - # self.isCrossConnect = True if self.annotations.get('optical.type') == 'cross-connect' else False | 116 | + |
| 578 | - if isinstance(node1, LINCSwitch) and isinstance(node2, LINCSwitch): | 117 | + if isinstance( node1, OpticalSwitch ): |
| 579 | - self.isCrossConnect = False | 118 | + cls1 = OpticalIntf |
| 580 | - else: | ||
| 581 | - self.isCrossConnect = True | ||
| 582 | - if isinstance(node1, LINCSwitch): | ||
| 583 | - cls1 = LINCIntf | ||
| 584 | - if self.isCrossConnect: | ||
| 585 | - node1.crossConnects.append(self) | ||
| 586 | else: | 119 | else: |
| 587 | cls1 = Intf | 120 | cls1 = Intf |
| 588 | # bad hack to stop error message from appearing when we try to set up intf in a packet switch, | 121 | # bad hack to stop error message from appearing when we try to set up intf in a packet switch, |
| 589 | # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up | 122 | # and there is no interface there( because we do not run makeIntfPair ). This way, we just set lo up |
| 590 | intfName1 = 'lo' | 123 | intfName1 = 'lo' |
| 591 | - if isinstance(node2, LINCSwitch): | 124 | + if isinstance( node2, OpticalSwitch ): |
| 592 | - cls2 = LINCIntf | 125 | + cls2 = OpticalIntf |
| 593 | - if self.isCrossConnect: | ||
| 594 | - node2.crossConnects.append(self) | ||
| 595 | else: | 126 | else: |
| 596 | cls2 = Intf | 127 | cls2 = Intf |
| 597 | intfName2 = 'lo' | 128 | intfName2 = 'lo' |
| 598 | - Link.__init__(self, node1, node2, port1=port1, port2=port2, | 129 | + Link.__init__( self, node1, node2, port1=port1, port2=port2, |
| 599 | intfName1=intfName1, intfName2=intfName2, cls1=cls1, | 130 | intfName1=intfName1, intfName2=intfName2, cls1=cls1, |
| 600 | - cls2=cls2, params1=params1, params2=params2) | 131 | + cls2=cls2, params1=params1, params2=params2 ) |
| 132 | + | ||
| 601 | 133 | ||
| 602 | @classmethod | 134 | @classmethod |
| 603 | - def makeIntfPair(_cls, intfName1, intfName2, *args, **kwargs): | 135 | + def makeIntfPair( _cls, intfName1, intfName2, *args, **kwargs ): |
| 604 | pass | 136 | pass |
| 605 | 137 | ||
| 606 | - def json(self): | 138 | + def json( self ): |
| 607 | "build and return the json configuration dictionary for this link" | 139 | "build and return the json configuration dictionary for this link" |
| 608 | configData = {} | 140 | configData = {} |
| 609 | - configData[ 'src' ] = ('of:' + self.intf1.node.dpid + | 141 | + configData[ 'src' ] = ( 'of:' + self.intf1.node.dpid + |
| 610 | - '/%s' % self.intf1.node.ports[ self.intf1 ]) | 142 | + '/%s' % self.intf1.node.ports[ self.intf1 ] ) |
| 611 | - configData[ 'dst' ] = ('of:' + self.intf2.node.dpid + | 143 | + configData[ 'dst' ] = ( 'of:' + self.intf2.node.dpid + |
| 612 | - '/%s' % self.intf2.node.ports[ self.intf2 ]) | 144 | + '/%s' % self.intf2.node.ports[ self.intf2 ] ) |
| 613 | configData[ 'type' ] = self.linkType | 145 | configData[ 'type' ] = self.linkType |
| 614 | configData[ 'annotations' ] = self.annotations | 146 | configData[ 'annotations' ] = self.annotations |
| 615 | return configData | 147 | return configData |
| 616 | 148 | ||
| 617 | -class LINCIntf(OpticalIntf): | 149 | +class OpticalIntf( Intf ): |
| 618 | - """ | 150 | + |
| 619 | - LINC interface class | 151 | + def __init__( self, name=None, node=None, speed=0, |
| 620 | - """ | 152 | + port=None, link=None, **params ): |
| 621 | - def __init__(self, name=None, node=None, speed=0, | ||
| 622 | - port=None, link=None, **params): | ||
| 623 | self.node = node | 153 | self.node = node |
| 624 | self.speed = speed | 154 | self.speed = speed |
| 625 | self.port = port | 155 | self.port = port |
| 626 | self.link = link | 156 | self.link = link |
| 627 | self.name = name | 157 | self.name = name |
| 628 | - node.addIntf(self, port=port) | 158 | + node.addIntf( self, port=port ) |
| 629 | self.params = params | 159 | self.params = params |
| 630 | self.ip = None | 160 | self.ip = None |
| 631 | 161 | ||
| 632 | - def json(self): | 162 | + def json( self ): |
| 633 | "build and return the JSON information for this interface( not used right now )" | 163 | "build and return the JSON information for this interface( not used right now )" |
| 634 | configDict = {} | 164 | configDict = {} |
| 635 | configDict[ 'port' ] = self.port | 165 | configDict[ 'port' ] = self.port |
| ... | @@ -637,34 +167,356 @@ class LINCIntf(OpticalIntf): | ... | @@ -637,34 +167,356 @@ class LINCIntf(OpticalIntf): |
| 637 | configDict[ 'type' ] = 'FIBER' | 167 | configDict[ 'type' ] = 'FIBER' |
| 638 | return configDict | 168 | return configDict |
| 639 | 169 | ||
| 640 | - def config(self, *args, **kwargs): | 170 | + def config( self, *args, **kwargs ): |
| 641 | "dont configure a dummy interface" | 171 | "dont configure a dummy interface" |
| 642 | pass | 172 | pass |
| 643 | 173 | ||
| 644 | - def ifconfig(self, status): | 174 | +def switchJSON( switch ): |
| 645 | - "configure the status" | 175 | + "Returns the json configuration for a packet switch" |
| 646 | - if status == "up": | 176 | + configDict = {} |
| 647 | - return self.node.w_port_up(self.port) | 177 | + configDict[ 'uri' ] = 'of:' + switch.dpid |
| 648 | - elif status == "down": | 178 | + configDict[ 'mac' ] = quietRun( 'cat /sys/class/net/%s/address' % switch.name ).strip( '\n' ).translate( None, ':' ) |
| 649 | - return self.node.w_port_down(self.port) | 179 | + configDict[ 'hw' ] = 'PK' # FIXME what about OVS? |
| 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" | ||
| 650 | 269 | ||
| 270 | + #append to list of switches | ||
| 271 | + switchConfig.append(builtSwitch); | ||
| 651 | 272 | ||
| 652 | -class MininetOE(Mininet): | 273 | + topoConfigJson["switchConfig"] = switchConfig; |
| 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 ): | ||
| 653 | "Mininet with Linc-OE support (starts and stops linc-oe)" | 473 | "Mininet with Linc-OE support (starts and stops linc-oe)" |
| 654 | 474 | ||
| 655 | - def start(self): | 475 | + def start( self ): |
| 656 | - Mininet.start(self) | 476 | + Mininet.start( self ) |
| 657 | - LINCSwitch.bootOE(self) | 477 | + startOE( self ) |
| 658 | 478 | ||
| 659 | - def stop(self): | 479 | + def stop( self ): |
| 660 | - Mininet.stop(self) | 480 | + Mininet.stop( self ) |
| 661 | - LINCSwitch.shutdownOE() | 481 | + stopOE() |
| 662 | 482 | ||
| 663 | - def addControllers(self, controllers): | 483 | + def addControllers( self, controllers ): |
| 664 | i = 0 | 484 | i = 0 |
| 665 | for ctrl in controllers: | 485 | for ctrl in controllers: |
| 666 | - self.addController(RemoteController('c%d' % i, ip=ctrl)) | 486 | + self.addController( RemoteController( 'c%d' % i, ip=ctrl ) ) |
| 667 | - i += 1 | 487 | + |
| 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 ) | ||
| 668 | 507 | ||
| 669 | if __name__ == '__main__': | 508 | if __name__ == '__main__': |
| 670 | - pass | 509 | + import sys |
| 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