TL1 device provider with driver for Lumentum WaveReady.
ONOS-5800 & ONOS-5801
Change-Id: Icd820285eb8db2fd92c03ebf11ce022b6a82b48a
diff --git a/protocols/pom.xml b/protocols/pom.xml
index b0a2ea6..13d44fe 100644
--- a/protocols/pom.xml
+++ b/protocols/pom.xml
@@ -43,6 +43,7 @@
<module>bmv2</module>
<module>lisp</module>
<module>restconf</module>
+ <module>tl1</module>
</modules>
<dependencies>
diff --git a/protocols/tl1/api/BUCK b/protocols/tl1/api/BUCK
new file mode 100644
index 0000000..3ce3f98
--- /dev/null
+++ b/protocols/tl1/api/BUCK
@@ -0,0 +1,8 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//lib:netty-transport',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+)
diff --git a/protocols/tl1/api/pom.xml b/protocols/tl1/api/pom.xml
new file mode 100644
index 0000000..bedfc6e
--- /dev/null
+++ b/protocols/tl1/api/pom.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>onos-tl1</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.9.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>onos-tl1-api</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-handler</artifactId>
+ </dependency>
+ </dependencies>
+
+</project>
\ No newline at end of file
diff --git a/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Command.java b/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Command.java
new file mode 100644
index 0000000..02dd4dc
--- /dev/null
+++ b/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Command.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.tl1;
+
+import com.google.common.annotations.Beta;
+
+import java.util.Optional;
+
+/**
+ * Representation of a TL1 command, which is sent from the controller to a network element.
+ *
+ * The following shows the typical TL1 command structure:
+ * {@literal VERB-MODIFIER:<tid>:<aid>:<ctag>::parameter-list;}
+ *
+ * The ctag must be a non-zero decimal number consisting of not more than six characters,
+ * and is assumed to be a unique message identifier per device.
+ */
+@Beta
+public interface Tl1Command {
+
+ /**
+ * Minimum CTAG value.
+ */
+ int MIN_CTAG = 0;
+
+ /**
+ * Maximum CTAG value.
+ */
+ int MAX_CTAG = 999999;
+
+ /**
+ * Returns the verb of the command.
+ *
+ * @return the verb
+ */
+ String verb();
+
+ /**
+ * Returns the modifier of the command.
+ *
+ * @return the modifier
+ */
+ String modifier();
+
+ /**
+ * Returns the optional target identifier (tid).
+ *
+ * @return the tid
+ */
+ Optional<String> tid();
+
+ /**
+ * Returns the optional access identifier (aid).
+ *
+ * @return the aid
+ */
+ Optional<String> aid();
+
+ /**
+ * Returns the correlation tag (ctag).
+ *
+ * @return correlation tag
+ */
+ int ctag();
+
+ /**
+ * Returns the optional parameters.
+ *
+ * @return the parameters
+ */
+ Optional<String> parameters();
+
+ /**
+ * TL1 command builder.
+ *
+ * @param <T> builder implementation type
+ */
+ interface Builder<T extends Builder<T>> {
+ /**
+ * Assigns a verb to this TL1 command.
+ *
+ * @param verb a verb
+ * @return this
+ */
+ T withVerb(String verb);
+
+ /**
+ * Assigns a modifier to this TL1 command.
+ *
+ * @param modifier a modifier
+ * @return this
+ */
+ T withModifier(String modifier);
+
+ /**
+ * Assigns a target identifier to this TL1 command.
+ *
+ * @param tid a tid
+ * @return this
+ */
+ T forTid(String tid);
+
+ /**
+ * Assigns an access identifier to this TL1 command.
+ *
+ * @param aid an aid
+ * @return this
+ */
+ T withAid(String aid);
+
+ /**
+ * Assigns a correlation tag to this TL1 command.
+ *
+ * @param ctag a ctag
+ * @return this
+ */
+ T withCtag(int ctag);
+
+ /**
+ * Assigns parameters to this TL1 command.
+ *
+ * @param parameters the parameters
+ * @return this
+ */
+ T withParameters(String parameters);
+
+ /**
+ * Builds a TL1 command.
+ *
+ * @return the TL1 command
+ */
+ Tl1Command build();
+ }
+}
diff --git a/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Controller.java b/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Controller.java
new file mode 100644
index 0000000..feaa0dc0
--- /dev/null
+++ b/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Controller.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.tl1;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.DeviceId;
+
+import java.util.Collection;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+
+@Beta
+public interface Tl1Controller {
+ /**
+ * Sends the message to the device asynchronously.
+ *
+ * @param deviceId the device to write to
+ * @param msg the message to write
+ * @return the device response
+ */
+ // TODO: return CompletableFuture<Tl1Message> once we have appropriate builders
+ CompletableFuture<String> sendMsg(DeviceId deviceId, Tl1Command msg);
+
+ /**
+ * Returns the device identified by given ID.
+ *
+ * @param deviceId the device ID to lookup
+ * @return optional Tl1Device
+ */
+ Optional<Tl1Device> getDevice(DeviceId deviceId);
+
+ /**
+ /**
+ * Adds a device to the controller.
+ * @param deviceId the device ID to add
+ * @param device the device to add
+ * @return true if device added, false if already known
+ */
+ boolean addDevice(DeviceId deviceId, Tl1Device device);
+
+ /**
+ * Disconnects the device and removes it from the controller.
+ * @param deviceId the device to remove
+ */
+ void removeDevice(DeviceId deviceId);
+
+ /**
+ * Connects the controller to the device.
+ * @param deviceId the device to disconnect to
+ */
+ void connectDevice(DeviceId deviceId);
+
+ /**
+ * Disconnects the device from the controller.
+ * @param deviceId the device to disconnect from
+ */
+ void disconnectDevice(DeviceId deviceId);
+
+ /**
+ * Returns a set of all devices IDs for this TL1 controller.
+ * @return set of device IDs
+ */
+ Set<DeviceId> getDeviceIds();
+
+ /**
+ * Returns a set of all devices for this TL1 controller.
+ * @return collection of TL1 devices
+ */
+ Collection<Tl1Device> getDevices();
+
+ /**
+ * Registers a listener for TL1 events.
+ *
+ * @param listener the listener to notify
+ */
+ void addListener(Tl1Listener listener);
+
+ /**
+ * Unregisters a listener for TL1 events.
+ *
+ * @param listener the listener to unregister
+ */
+ void removeListener(Tl1Listener listener);
+}
diff --git a/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Device.java b/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Device.java
new file mode 100644
index 0000000..71dc01e
--- /dev/null
+++ b/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Device.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.tl1;
+
+import com.google.common.annotations.Beta;
+import io.netty.channel.Channel;
+import org.onlab.packet.IpAddress;
+
+@Beta
+public interface Tl1Device {
+ IpAddress ip();
+
+ int port();
+
+ /**
+ * The username to log in to the switch.
+ *
+ * @return the user name
+ */
+ String username();
+
+ /**
+ * The password to log in to the switch.
+ *
+ * @return the password
+ */
+ String password();
+
+ /**
+ * The target identifier (TID) of the device.
+ *
+ * @return the tid
+ */
+ String tid();
+
+ /**
+ * Check if the switch is connected.
+ *
+ * @return true if connected, false otherwise
+ */
+ boolean isConnected();
+
+ /**
+ * Returns the netty channel of the switch.
+ *
+ * @return the netty channel, null if disconnected
+ */
+ Channel channel();
+
+ /**
+ * Connects the switch to the channel.
+ * @param channel the channel
+ */
+ void connect(Channel channel);
+
+ /**
+ * Disconnects the switch from its channel.
+ */
+ void disconnect();
+}
diff --git a/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Listener.java b/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Listener.java
new file mode 100644
index 0000000..8260e05
--- /dev/null
+++ b/protocols/tl1/api/src/main/java/org/onosproject/tl1/Tl1Listener.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.tl1;
+
+import com.google.common.annotations.Beta;
+import org.onosproject.net.DeviceId;
+
+@Beta
+public interface Tl1Listener {
+ /**
+ * Notify that the device has connected.
+ *
+ * @param deviceId the connected device
+ */
+ void deviceConnected(DeviceId deviceId);
+
+ /**
+ * Notify that the device has disconnected.
+ *
+ * @param deviceId the disconnected device
+ */
+ void deviceDisconnected(DeviceId deviceId);
+}
diff --git a/protocols/tl1/api/src/main/java/org/onosproject/tl1/package-info.java b/protocols/tl1/api/src/main/java/org/onosproject/tl1/package-info.java
new file mode 100644
index 0000000..964dcf6
--- /dev/null
+++ b/protocols/tl1/api/src/main/java/org/onosproject/tl1/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * TL1 protocol API.
+ */
+package org.onosproject.tl1;
\ No newline at end of file
diff --git a/protocols/tl1/ctl/BUCK b/protocols/tl1/ctl/BUCK
new file mode 100644
index 0000000..2dc94c2
--- /dev/null
+++ b/protocols/tl1/ctl/BUCK
@@ -0,0 +1,18 @@
+COMPILE_DEPS = [
+ '//lib:CORE_DEPS',
+ '//lib:netty-transport',
+ '//lib:netty-buffer',
+ '//lib:netty-codec',
+ '//protocols/tl1/api:onos-protocols-tl1-api',
+]
+
+TEST_DEPS = [
+ '//lib:TEST_ADAPTERS',
+ '//utils/osgi:onlab-osgi-tests',
+ '//core/api:onos-api-tests',
+]
+
+osgi_jar_with_tests (
+ deps = COMPILE_DEPS,
+ test_deps = TEST_DEPS,
+)
diff --git a/protocols/tl1/ctl/pom.xml b/protocols/tl1/ctl/pom.xml
new file mode 100644
index 0000000..033a4f9
--- /dev/null
+++ b/protocols/tl1/ctl/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>onos-tl1</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.9.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>onos-tl1-ctl</artifactId>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-tl1-api</artifactId>
+ <version>${project.version}</version>
+ </dependency>
+ <dependency>
+ <groupId>io.netty</groupId>
+ <artifactId>netty-handler</artifactId>
+ </dependency>
+ </dependencies>
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
diff --git a/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Command.java b/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Command.java
new file mode 100644
index 0000000..ab55427
--- /dev/null
+++ b/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Command.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.tl1.impl;
+
+import org.onosproject.tl1.Tl1Command;
+
+import java.util.Optional;
+
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of a TL1 command.
+ */
+public final class DefaultTl1Command implements Tl1Command {
+ private static final char HYPHEN = '-';
+ private static final char COLON = ':';
+ private static final char SEMICOLON = ';';
+
+ private String verb;
+ private String modifier;
+ private Optional<String> tid;
+ private String aid;
+ private int ctag;
+ private String parameters;
+
+ private DefaultTl1Command(String verb, String modifier, String tid, String aid, int ctag, String parameters) {
+ this.verb = verb;
+ this.modifier = modifier;
+ this.tid = Optional.ofNullable(tid);
+ this.aid = aid;
+ this.ctag = ctag;
+ this.parameters = parameters;
+ }
+
+ @Override
+ public String verb() {
+ return verb;
+ }
+
+ @Override
+ public String modifier() {
+ return modifier;
+ }
+
+ @Override
+ public Optional<String> tid() {
+ return tid;
+ }
+
+ @Override
+ public Optional<String> aid() {
+ return Optional.ofNullable(aid);
+ }
+
+ @Override
+ public int ctag() {
+ return ctag;
+ }
+
+ @Override
+ public Optional<String> parameters() {
+ return Optional.ofNullable(parameters);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder()
+ .append(verb).append(HYPHEN)
+ .append(modifier).append(COLON)
+ .append(tid().orElse("")).append(COLON)
+ .append(aid().orElse("")).append(COLON)
+ .append(ctag);
+
+ if (parameters().isPresent()) {
+ sb.append(COLON).append(COLON).append(parameters);
+ }
+
+ return sb.append(SEMICOLON).toString();
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static final class Builder implements Tl1Command.Builder {
+ private String verb;
+ private String modifier;
+ private String tid;
+ private String aid;
+ private int ctag;
+ private String parameters;
+
+ @Override
+ public Tl1Command.Builder withVerb(String verb) {
+ this.verb = verb;
+ return this;
+ }
+
+ @Override
+ public Tl1Command.Builder withModifier(String modifier) {
+ this.modifier = modifier;
+ return this;
+ }
+
+ @Override
+ public Tl1Command.Builder forTid(String tid) {
+ this.tid = tid;
+ return this;
+ }
+
+ @Override
+ public Tl1Command.Builder withAid(String aid) {
+ this.aid = aid;
+ return this;
+ }
+
+ @Override
+ public Tl1Command.Builder withCtag(int ctag) {
+ this.ctag = ctag;
+ return this;
+ }
+
+ @Override
+ public Tl1Command.Builder withParameters(String parameters) {
+ this.parameters = parameters;
+ return this;
+ }
+
+ @Override
+ public Tl1Command build() {
+ checkNotNull(verb, "Must supply a verb");
+ checkNotNull(modifier, "Must supply a modifier");
+
+ checkArgument(MIN_CTAG < ctag, "ctag cannot be less than " + MIN_CTAG);
+ checkArgument(ctag <= MAX_CTAG, "ctag cannot be larger than " + MAX_CTAG);
+
+ return new DefaultTl1Command(verb, modifier, tid, aid, ctag, parameters);
+ }
+ }
+}
diff --git a/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Controller.java b/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Controller.java
new file mode 100644
index 0000000..7a2fa8f
--- /dev/null
+++ b/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Controller.java
@@ -0,0 +1,263 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.tl1.impl;
+
+import io.netty.bootstrap.Bootstrap;
+import io.netty.buffer.ByteBuf;
+import io.netty.buffer.Unpooled;
+import io.netty.channel.Channel;
+import io.netty.channel.ChannelFuture;
+import io.netty.channel.ChannelHandlerContext;
+import io.netty.channel.ChannelInitializer;
+import io.netty.channel.ChannelOption;
+import io.netty.channel.EventLoopGroup;
+import io.netty.channel.SimpleChannelInboundHandler;
+import io.netty.channel.nio.NioEventLoopGroup;
+import io.netty.channel.socket.SocketChannel;
+import io.netty.channel.socket.nio.NioSocketChannel;
+import io.netty.handler.codec.DelimiterBasedFrameDecoder;
+import io.netty.handler.codec.string.StringDecoder;
+import io.netty.util.CharsetUtil;
+import org.apache.commons.lang.StringUtils;
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.onlab.util.Tools;
+import org.onosproject.mastership.MastershipService;
+import org.onosproject.net.DeviceId;
+import org.onosproject.tl1.Tl1Command;
+import org.onosproject.tl1.Tl1Controller;
+import org.onosproject.tl1.Tl1Device;
+import org.onosproject.tl1.Tl1Listener;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.CompletableFuture;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentMap;
+import java.util.concurrent.CopyOnWriteArraySet;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+import java.util.stream.Collectors;
+
+/**
+ * Implementation of TL1 controller.
+ *
+ * Handles the connection and input/output for all registered TL1 devices.
+ * Turn on debug logging if you want to see all message I/O.
+ *
+ * Per device, we track commands using a simple ctag-keyed map. This assumes the client is sending out unique ctag's.
+ */
+@Component(immediate = true)
+@Service
+public class DefaultTl1Controller implements Tl1Controller {
+ private final Logger log = LoggerFactory.getLogger(DefaultTl1Controller.class);
+
+ // TL1 message delimiter (semi colon)
+ private static final ByteBuf DELIMITER = Unpooled.copiedBuffer(new char[]{';'}, Charset.defaultCharset());
+ private static final String COMPLD = "COMPLD";
+ private static final String DENY = "DENY";
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
+ protected MastershipService mastershipService;
+
+ private ConcurrentMap<DeviceId, Tl1Device> deviceMap = new ConcurrentHashMap<>();
+ // Key: channel, value: map with key ctag, value: future TL1 msg (ctags are assumed unique per device)
+ private ConcurrentMap<Channel, ConcurrentMap<Integer, CompletableFuture<String>>> msgMap =
+ new ConcurrentHashMap<>();
+ private EventLoopGroup workerGroup = new NioEventLoopGroup();
+ private Set<Tl1Listener> tl1Listeners = new CopyOnWriteArraySet<>();
+ private ExecutorService executor;
+
+ @Activate
+ public void activate() {
+ executor = Executors.newFixedThreadPool(
+ Runtime.getRuntime().availableProcessors(),
+ Tools.groupedThreads("onos/tl1controller", "%d", log));
+ log.info("Started");
+ }
+
+ @Deactivate
+ public void deactivate() {
+ executor.shutdown();
+ deviceMap.clear();
+ msgMap.clear();
+ log.info("Stopped");
+ }
+
+ @Override
+ /**
+ * This implementation returns an empty string on failure.
+ */
+ public CompletableFuture<String> sendMsg(DeviceId deviceId, Tl1Command msg) {
+ log.debug("Sending TL1 message to device {}: {}", deviceId, msg);
+
+ Tl1Device device = deviceMap.get(deviceId);
+ if (device == null || !device.isConnected() || !mastershipService.isLocalMaster(deviceId)) {
+ return CompletableFuture.completedFuture(StringUtils.EMPTY);
+ }
+
+ // Create and store completable future, complete it in the channel handler when we receive a response
+ CompletableFuture<String> future = new CompletableFuture<>();
+ Channel channel = device.channel();
+ if (!msgMap.containsKey(channel)) {
+ return CompletableFuture.completedFuture(StringUtils.EMPTY);
+ }
+ msgMap.get(channel).put(msg.ctag(), future);
+
+ // Write message to channel
+ channel.writeAndFlush(Unpooled.copiedBuffer(msg.toString(), CharsetUtil.UTF_8));
+
+ return future;
+ }
+
+ @Override
+ public Optional<Tl1Device> getDevice(DeviceId deviceId) {
+ return Optional.ofNullable(deviceMap.get(deviceId));
+ }
+
+ @Override
+ public boolean addDevice(DeviceId deviceId, Tl1Device device) {
+ log.debug("Adding TL1 device {} {}", deviceId);
+
+ // Ignore if device already known
+ if (deviceMap.containsKey(deviceId)) {
+ log.error("Ignoring duplicate device {}", deviceId);
+ return false;
+ }
+
+ deviceMap.put(deviceId, device);
+ return true;
+ }
+
+ @Override
+ public void connectDevice(DeviceId deviceId) {
+ Tl1Device device = deviceMap.get(deviceId);
+ if (device == null || device.isConnected()) {
+ return;
+ }
+
+ Bootstrap b = new Bootstrap();
+ b.group(workerGroup)
+ .channel(NioSocketChannel.class)
+ .option(ChannelOption.SO_KEEPALIVE, true)
+ .handler(new ChannelInitializer<SocketChannel>() {
+ @Override
+ protected void initChannel(SocketChannel socketChannel) throws Exception {
+ socketChannel.pipeline().addLast(new DelimiterBasedFrameDecoder(8192, DELIMITER));
+ socketChannel.pipeline().addLast("stringDecoder", new StringDecoder(CharsetUtil.UTF_8));
+ // TODO
+ //socketChannel.pipeline().addLast(new Tl1Decoder());
+ socketChannel.pipeline().addLast(new Tl1InboundHandler());
+ }
+ })
+ .remoteAddress(device.ip().toInetAddress(), device.port())
+ .connect()
+ .addListener((ChannelFuture channelFuture) -> {
+ if (channelFuture.isSuccess()) {
+ msgMap.put(channelFuture.channel(), new ConcurrentHashMap<>());
+ device.connect(channelFuture.channel());
+ tl1Listeners.forEach(l -> executor.execute(() -> l.deviceConnected(deviceId)));
+ }
+ });
+ }
+
+ @Override
+ public void removeDevice(DeviceId deviceId) {
+ disconnectDevice(deviceId);
+ deviceMap.remove(deviceId);
+ }
+
+ @Override
+ public void addListener(Tl1Listener listener) {
+ tl1Listeners.add(listener);
+ }
+
+ @Override
+ public void removeListener(Tl1Listener listener) {
+ tl1Listeners.remove(listener);
+ }
+
+ @Override
+ public void disconnectDevice(DeviceId deviceId) {
+ // Ignore if unknown device
+ Tl1Device device = deviceMap.get(deviceId);
+ if (device == null) {
+ return;
+ }
+
+ Channel channel = device.channel();
+ if (channel != null) {
+ channel.close();
+ }
+
+ msgMap.remove(channel);
+ device.disconnect();
+ tl1Listeners.forEach(l -> l.deviceDisconnected(deviceId));
+ }
+
+ @Override
+ public Set<DeviceId> getDeviceIds() {
+ return deviceMap.keySet();
+ }
+
+ @Override
+ public Collection<Tl1Device> getDevices() {
+ return deviceMap.values();
+ }
+
+ /**
+ * Crude filtering handler that will only complete our stored future upon receiving a TL1 response messages.
+ */
+ private class Tl1InboundHandler extends SimpleChannelInboundHandler<String> {
+ @Override
+ protected void channelRead0(ChannelHandlerContext ctx, String s) throws Exception {
+ log.debug("Received TL1 message {}", s);
+
+ // Search for "COMPLD" or "DENY" to identify a TL1 response,
+ // then return the remainder of the string.
+ String[] words = s.split("\\s");
+ for (int i = 0; i < words.length; i++) {
+ String w = words[i];
+ if (w.startsWith(COMPLD) || w.startsWith(DENY)) {
+ // ctag is just in front of it
+ int ctag = Integer.parseInt(words[i - 1]);
+ // We return everything that follows to the caller (this will lose line breaks and such)
+ String result = Arrays.stream(words).skip(i + 1).collect(Collectors.joining());
+ // Set future when command is executed, good or bad
+ Map<Integer, CompletableFuture<String>> msg = msgMap.get(ctx.channel());
+ if (msg != null) {
+ CompletableFuture<String> f = msg.remove(ctag);
+ if (f != null) {
+ f.complete(result);
+ }
+ }
+
+ return;
+ }
+ }
+ }
+ }
+}
diff --git a/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Device.java b/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Device.java
new file mode 100644
index 0000000..602d24c
--- /dev/null
+++ b/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/DefaultTl1Device.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2016-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.onosproject.tl1.impl;
+
+import io.netty.channel.Channel;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.TpPort;
+import org.onosproject.tl1.Tl1Device;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Objects;
+
+import static com.google.common.base.MoreObjects.toStringHelper;
+import static com.google.common.base.Preconditions.checkArgument;
+import static com.google.common.base.Preconditions.checkNotNull;
+
+/**
+ * Default implementation of a TL1 device.
+ */
+public class DefaultTl1Device implements Tl1Device {
+ private final Logger log = LoggerFactory.getLogger(DefaultTl1Device.class);
+ private static final String TL1 = "tl1";
+
+ private IpAddress ip;
+ private int port;
+ private String username;
+ private String password;
+ private String tid;
+ private Channel channel;
+
+ @Override
+ public void connect(Channel channel) {
+ this.channel = channel;
+ }
+
+ @Override
+ public void disconnect() {
+ this.channel = null;
+ }
+
+ @Override
+ public boolean isConnected() {
+ return channel != null;
+ }
+
+ @Override
+ public IpAddress ip() {
+ return ip;
+ }
+
+ @Override
+ public int port() {
+ return port;
+ }
+
+ @Override
+ public String username() {
+ return username;
+ }
+
+ @Override
+ public String password() {
+ return password;
+ }
+
+ @Override
+ public Channel channel() {
+ return channel;
+ }
+
+ @Override
+ public String tid() {
+ return tid;
+ }
+
+ public DefaultTl1Device(IpAddress ip, int port, String username, String password) {
+ this.ip = checkNotNull(ip);
+ checkArgument((TpPort.MIN_PORT <= port) && (port <= TpPort.MAX_PORT));
+ this.port = port;
+ this.username = checkNotNull(username);
+ this.password = checkNotNull(password);
+ this.tid = null;
+ channel = null;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(ip, port, username, password);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (obj instanceof DefaultTl1Device) {
+ DefaultTl1Device that = (DefaultTl1Device) obj;
+ return Objects.equals(ip, that.ip) &&
+ Objects.equals(port, that.port) &&
+ Objects.equals(username, that.username) &&
+ Objects.equals(password, that.password) &&
+ Objects.equals(tid, that.tid) &&
+ Objects.equals(channel, that.channel);
+ }
+ return false;
+ }
+
+ @Override
+ public String toString() {
+ return toStringHelper(this)
+ .add("ip", ip)
+ .add("port", port)
+ .add("username", username)
+ .add("password", password)
+ .add("tid", tid == null ? "N/A" : tid)
+ .toString();
+ }
+}
diff --git a/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/package-info.java b/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/package-info.java
new file mode 100644
index 0000000..4d7f24e
--- /dev/null
+++ b/protocols/tl1/ctl/src/main/java/org/onosproject/tl1/impl/package-info.java
@@ -0,0 +1,20 @@
+/*
+ * Copyright 2017-present Open Networking Laboratory
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * TL1 protocol API implementation.
+ */
+package org.onosproject.tl1.impl;
\ No newline at end of file
diff --git a/protocols/tl1/pom.xml b/protocols/tl1/pom.xml
new file mode 100644
index 0000000..dd2c269
--- /dev/null
+++ b/protocols/tl1/pom.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ ~ Copyright 2016-present Open Networking Laboratory
+ ~
+ ~ Licensed under the Apache License, Version 2.0 (the "License");
+ ~ you may not use this file except in compliance with the License.
+ ~ You may obtain a copy of the License at
+ ~
+ ~ http://www.apache.org/licenses/LICENSE-2.0
+ ~
+ ~ Unless required by applicable law or agreed to in writing, software
+ ~ distributed under the License is distributed on an "AS IS" BASIS,
+ ~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ ~ See the License for the specific language governing permissions and
+ ~ limitations under the License.
+ -->
+<project xmlns="http://maven.apache.org/POM/4.0.0"
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+ <parent>
+ <artifactId>onos-protocols</artifactId>
+ <groupId>org.onosproject</groupId>
+ <version>1.9.0-SNAPSHOT</version>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+
+ <artifactId>onos-tl1</artifactId>
+ <packaging>pom</packaging>
+ <modules>
+ <module>ctl</module>
+ <module>api</module>
+ </modules>
+
+ <dependencies>
+ <dependency>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>org.apache.felix.scr.annotations</artifactId>
+ </dependency>
+ <dependency>
+ <groupId>org.onosproject</groupId>
+ <artifactId>onos-api</artifactId>
+ </dependency>
+ </dependencies>
+
+
+ <build>
+ <plugins>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-bundle-plugin</artifactId>
+ </plugin>
+ <plugin>
+ <groupId>org.apache.felix</groupId>
+ <artifactId>maven-scr-plugin</artifactId>
+ </plugin>
+ </plugins>
+ </build>
+</project>
\ No newline at end of file