Andrea Campanella
Committed by Gerrit Code Review

[ONOS-4045]Adding mastership service to NetconfProvider

Change-Id: Id39cbef54a079ab6e080a9d3f60770c4bea90b3f
......@@ -18,6 +18,7 @@ package org.onosproject.drivers.netconf;
import com.google.common.base.Preconditions;
import org.onosproject.drivers.utilities.XmlConfigParser;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.DeviceId;
import org.onosproject.net.behaviour.ControllerConfig;
import org.onosproject.net.behaviour.ControllerInfo;
......@@ -44,21 +45,29 @@ public class NetconfControllerConfig extends AbstractHandlerBehaviour
private final Logger log = getLogger(NetconfControllerConfig.class);
@Override
public List<ControllerInfo> getControllers() {
DriverHandler handler = handler();
NetconfController controller = handler.get(NetconfController.class);
DeviceId ofDeviceId = handler.data().deviceId();
MastershipService mastershipService = handler.get(MastershipService.class);
DeviceId deviceId = handler.data().deviceId();
Preconditions.checkNotNull(controller, "Netconf controller is null");
List<ControllerInfo> controllers = new ArrayList<>();
try {
String reply = controller.getDevicesMap().get(ofDeviceId).getSession().
getConfig("running");
log.debug("Reply XML {}", reply);
controllers.addAll(XmlConfigParser.parseStreamControllers(XmlConfigParser.
loadXml(new ByteArrayInputStream(reply.getBytes(StandardCharsets.UTF_8)))));
} catch (IOException e) {
log.error("Cannot communicate with device {} ", ofDeviceId);
if (mastershipService.isLocalMaster(deviceId)) {
try {
String reply = controller.getNetconfDevice(deviceId).getSession().
getConfig("running");
log.debug("Reply XML {}", reply);
controllers.addAll(XmlConfigParser.parseStreamControllers(XmlConfigParser.
loadXml(new ByteArrayInputStream(reply.getBytes(StandardCharsets.UTF_8)))));
} catch (IOException e) {
log.error("Cannot communicate with device {} ", deviceId, e);
}
} else {
log.warn("I'm not master for {} please use master, {} to execute command",
deviceId,
mastershipService.getMasterFor(deviceId));
}
return controllers;
}
......@@ -69,30 +78,36 @@ public class NetconfControllerConfig extends AbstractHandlerBehaviour
NetconfController controller = handler.get(NetconfController.class);
DeviceId deviceId = handler.data().deviceId();
Preconditions.checkNotNull(controller, "Netconf controller is null");
try {
NetconfDevice device = controller.getNetconfDevice(deviceId);
String config = null;
MastershipService mastershipService = handler.get(MastershipService.class);
if (mastershipService.isLocalMaster(deviceId)) {
try {
String reply = device.getSession().getConfig("running");
log.info("reply XML {}", reply);
config = XmlConfigParser.createControllersConfig(
XmlConfigParser.loadXml(getClass().getResourceAsStream("controllers.xml")),
XmlConfigParser.loadXml(
new ByteArrayInputStream(reply.getBytes(StandardCharsets.UTF_8))),
"running", "merge", "create", controllers
);
NetconfDevice device = controller.getNetconfDevice(deviceId);
String config = null;
try {
String reply = device.getSession().getConfig("running");
log.info("reply XML {}", reply);
config = XmlConfigParser.createControllersConfig(
XmlConfigParser.loadXml(getClass().getResourceAsStream("controllers.xml")),
XmlConfigParser.loadXml(
new ByteArrayInputStream(reply.getBytes(StandardCharsets.UTF_8))),
"running", "merge", "create", controllers
);
} catch (IOException e) {
log.error("Cannot comunicate to device {} , exception {}", deviceId, e.getMessage());
}
device.getSession().editConfig(config.substring(config.indexOf("-->") + 3));
} catch (NullPointerException e) {
log.warn("No NETCONF device with requested parameters " + e);
throw new NullPointerException("No NETCONF device with requested parameters " + e);
} catch (IOException e) {
log.error("Cannot comunicate to device {} , exception {}", deviceId, e.getMessage());
}
device.getSession().editConfig(config.substring(config.indexOf("-->") + 3));
} catch (NullPointerException e) {
log.warn("No NETCONF device with requested parameters " + e);
throw new NullPointerException("No NETCONF device with requested parameters " + e);
} catch (IOException e) {
log.error("Cannot comunicate to device {} , exception {}", deviceId, e.getMessage());
} else {
log.warn("I'm not master for {} please use master, {} to execute command",
deviceId,
mastershipService.getMasterFor(deviceId));
}
}
//TODO maybe put method getNetconfClientService like in ovsdb if we need it
......
......@@ -46,31 +46,31 @@ public interface NetconfController {
* Tries to connect to a specific NETCONF device, if the connection is succesful
* it creates and adds the device to the ONOS core as a NetconfDevice.
*
* @param deviceInfo info about the device to add
* @param deviceId deviceId of the device to connect
* @return NetconfDevice Netconf device
* @throws NetconfException when device is not available
*/
NetconfDevice connectDevice(NetconfDeviceInfo deviceInfo) throws NetconfException;
NetconfDevice connectDevice(DeviceId deviceId) throws NetconfException;
/**
* Disconnects a Netconf device and removes it from the core.
*
* @param deviceInfo info about the device to remove
* @param deviceId id of the device to remove
* @param remove true if device is to be removed from core
*/
void disconnectDevice(NetconfDeviceInfo deviceInfo);
void disconnectDevice(DeviceId deviceId, boolean remove);
/**
* Removes a Netconf device from the core.
*
* @param deviceInfo info about the device to remove
* @param deviceId id of the device to remove
*/
void removeDevice(NetconfDeviceInfo deviceInfo);
void removeDevice(DeviceId deviceId);
/**
* Gets all the nodes information.
*
* @return map of devices
*
*/
Map<DeviceId, NetconfDevice> getDevicesMap();
......@@ -97,5 +97,4 @@ public interface NetconfController {
* @return NetconfDevice Netconf device
*/
NetconfDevice getNetconfDevice(IpAddress ip, int port);
}
......
......@@ -16,22 +16,25 @@
package org.onosproject.netconf;
import org.onosproject.net.DeviceId;
/**
* Allows for providers interested in node events to be notified.
*/
public interface NetconfDeviceListener {
/**
* Notifies that the node was added.
* Notifies that the device was added.
*
* @param nodeId the node where the event occurred
* @param deviceId the device that was added
*/
void deviceAdded(NetconfDeviceInfo nodeId);
void deviceAdded(DeviceId deviceId);
/**
* Notifies that the node was removed.
* Notifies that the device was removed.
*
* @param nodeId the node where the event occurred
* @param deviceId the device that was removed
*/
void deviceRemoved(NetconfDeviceInfo nodeId);
void deviceRemoved(DeviceId deviceId);
}
......
......@@ -35,7 +35,7 @@ public class DefaultNetconfDevice implements NetconfDevice {
.getLogger(DefaultNetconfDevice.class);
private NetconfDeviceInfo netconfDeviceInfo;
private boolean deviceState = false;
private boolean deviceState = true;
protected NetconfSessionFactory sessionFactory = new SshNetconfSessionFactory();
private NetconfSession netconfSession;
......@@ -53,10 +53,10 @@ public class DefaultNetconfDevice implements NetconfDevice {
try {
netconfSession = sessionFactory.createNetconfSession(deviceInfo);
} catch (IOException e) {
deviceState = false;
throw new NetconfException("Cannot create connection and session for device " +
deviceInfo, e);
}
deviceState = true;
}
@Override
......
......@@ -19,9 +19,16 @@ package org.onosproject.netconf.ctl;
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.packet.IpAddress;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.key.DeviceKeyId;
import org.onosproject.net.key.DeviceKeyService;
import org.onosproject.net.key.UsernamePassword;
import org.onosproject.netconf.NetconfController;
import org.onosproject.netconf.NetconfDevice;
import org.onosproject.netconf.NetconfDeviceFactory;
......@@ -34,6 +41,7 @@ import org.osgi.service.component.ComponentContext;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Arrays;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
......@@ -45,6 +53,11 @@ import java.util.concurrent.CopyOnWriteArraySet;
@Component(immediate = true)
@Service
public class NetconfControllerImpl implements NetconfController {
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceKeyService deviceKeyService;
public static final Logger log = LoggerFactory
.getLogger(NetconfControllerImpl.class);
......@@ -95,55 +108,93 @@ public class NetconfControllerImpl implements NetconfController {
}
@Override
public NetconfDevice connectDevice(NetconfDeviceInfo deviceInfo) throws NetconfException {
if (netconfDeviceMap.containsKey(deviceInfo.getDeviceId())) {
log.info("Device {} is already present", deviceInfo);
return netconfDeviceMap.get(deviceInfo.getDeviceId());
public NetconfDevice connectDevice(DeviceId deviceId) throws NetconfException {
if (netconfDeviceMap.containsKey(deviceId)) {
log.debug("Device {} is already present", deviceId);
return netconfDeviceMap.get(deviceId);
} else {
log.info("Creating NETCONF device {}", deviceInfo);
NetconfDevice device = createDevice(deviceInfo);
device.getSession().addDeviceOutputListener(downListener);
return device;
log.debug("Creating NETCONF device {}", deviceId);
Device device = deviceService.getDevice(deviceId);
String ip;
int port;
if (device != null) {
ip = device.annotations().value("ipaddress");
port = Integer.parseInt(device.annotations().value("port"));
} else {
String[] info = deviceId.toString().split(":");
if (info.length == 3) {
ip = info[1];
port = Integer.parseInt(info[2]);
} else {
ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
&& !el.equals(info[info.length - 1]))
.reduce((t, u) -> t + ":" + u)
.get();
log.debug("ip v6 {}", ip);
port = Integer.parseInt(info[info.length - 1]);
}
}
try {
UsernamePassword deviceKey = deviceKeyService.getDeviceKey(
DeviceKeyId.deviceKeyId(deviceId.toString())).asUsernamePassword();
NetconfDeviceInfo deviceInfo = new NetconfDeviceInfo(deviceKey.username(),
deviceKey.password(),
IpAddress.valueOf(ip),
port);
NetconfDevice netconfDevicedevice = createDevice(deviceInfo);
netconfDevicedevice.getSession().addDeviceOutputListener(downListener);
return netconfDevicedevice;
} catch (NullPointerException e) {
throw new NetconfException("No Device Key for device " + deviceId, e);
}
}
}
@Override
public void disconnectDevice(NetconfDeviceInfo deviceInfo) {
if (!netconfDeviceMap.containsKey(deviceInfo.getDeviceId())) {
log.warn("Device {} is not present", deviceInfo);
public void disconnectDevice(DeviceId deviceId, boolean remove) {
if (!netconfDeviceMap.containsKey(deviceId)) {
log.warn("Device {} is not present", deviceId);
} else {
stopDevice(deviceInfo);
stopDevice(deviceId, remove);
}
}
private void stopDevice(DeviceId deviceId, boolean remove) {
netconfDeviceMap.get(deviceId).disconnect();
netconfDeviceMap.remove(deviceId);
if (remove) {
for (NetconfDeviceListener l : netconfDeviceListeners) {
l.deviceRemoved(deviceId);
}
}
}
@Override
public void removeDevice(NetconfDeviceInfo deviceInfo) {
if (!netconfDeviceMap.containsKey(deviceInfo.getDeviceId())) {
log.warn("Device {} is not present", deviceInfo);
public void removeDevice(DeviceId deviceId) {
if (!netconfDeviceMap.containsKey(deviceId)) {
log.warn("Device {} is not present", deviceId);
for (NetconfDeviceListener l : netconfDeviceListeners) {
l.deviceRemoved(deviceId);
}
} else {
netconfDeviceMap.remove(deviceInfo.getDeviceId());
netconfDeviceMap.remove(deviceId);
for (NetconfDeviceListener l : netconfDeviceListeners) {
l.deviceRemoved(deviceInfo);
l.deviceRemoved(deviceId);
}
}
}
private NetconfDevice createDevice(NetconfDeviceInfo deviceInfo) throws NetconfException {
NetconfDevice netconfDevice = deviceFactory.createNetconfDevice(deviceInfo);
netconfDeviceMap.put(deviceInfo.getDeviceId(), netconfDevice);
for (NetconfDeviceListener l : netconfDeviceListeners) {
l.deviceAdded(deviceInfo);
l.deviceAdded(deviceInfo.getDeviceId());
}
netconfDeviceMap.put(deviceInfo.getDeviceId(), netconfDevice);
return netconfDevice;
}
private void stopDevice(NetconfDeviceInfo deviceInfo) {
netconfDeviceMap.get(deviceInfo.getDeviceId()).disconnect();
netconfDeviceMap.remove(deviceInfo.getDeviceId());
for (NetconfDeviceListener l : netconfDeviceListeners) {
l.deviceRemoved(deviceInfo);
}
}
@Override
public Map<DeviceId, NetconfDevice> getDevicesMap() {
......@@ -155,6 +206,8 @@ public class NetconfControllerImpl implements NetconfController {
return netconfDeviceMap.keySet();
}
//Device factory for the specific NetconfDeviceImpl
private class DefaultNetconfDeviceFactory implements NetconfDeviceFactory {
......@@ -171,7 +224,7 @@ public class NetconfControllerImpl implements NetconfController {
@Override
public void event(NetconfDeviceOutputEvent event) {
if (event.type().equals(NetconfDeviceOutputEvent.Type.DEVICE_UNREGISTERED)) {
removeDevice(event.getDeviceInfo());
removeDevice(event.getDeviceInfo().getDeviceId());
}
}
......
......@@ -375,13 +375,12 @@ public class NetconfSessionImpl implements NetconfSession {
private boolean close(boolean force) throws NetconfException {
StringBuilder rpc = new StringBuilder();
rpc.append("<rpc>");
rpc.append("<rpc xmlns=\"urn:ietf:params:xml:ns:netconf:base:1.0\">");
if (force) {
rpc.append("<kill-configuration/>");
rpc.append("<kill-session/>");
} else {
rpc.append("<close-configuration/>");
rpc.append("<close-session/>");
}
rpc.append("<close-configuration/>");
rpc.append("</rpc>");
rpc.append(ENDPATTERN);
return checkReply(sendRequest(rpc.toString())) || close(true);
......
......@@ -22,6 +22,8 @@ import org.junit.Before;
import org.junit.Test;
import org.onlab.packet.IpAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.key.DeviceKeyService;
import org.onosproject.netconf.NetconfDevice;
import org.onosproject.netconf.NetconfDeviceFactory;
import org.onosproject.netconf.NetconfDeviceInfo;
......@@ -44,12 +46,14 @@ import static org.junit.Assert.*;
* Unit tests for the Netconf controller implementation test.
*/
public class NetconfControllerImplTest {
NetconfControllerImpl ctrl;
//DeviceInfo
NetconfDeviceInfo deviceInfo1;
NetconfDeviceInfo deviceInfo2;
NetconfDeviceInfo badDeviceInfo3;
NetconfDeviceInfo deviceInfoIpV6;
//Devices & DeviceId
NetconfDevice device1;
......@@ -68,21 +72,29 @@ public class NetconfControllerImplTest {
private static final String DEVICE_1_IP = "10.10.10.11";
private static final String DEVICE_2_IP = "10.10.10.12";
private static final String BAD_DEVICE_IP = "10.10.10.13";
private static final String DEVICE_IPV6 = "2001:db8::1";
private static final int DEVICE_1_PORT = 11;
private static final int DEVICE_2_PORT = 12;
private static final int BAD_DEVICE_PORT = 13;
private static final int IPV6_DEVICE_PORT = 14;
private static DeviceService deviceService = new NetconfDeviceServiceMock();
private static DeviceKeyService deviceKeyService = new NetconfDeviceKeyServiceMock();
@Before
public void setUp() throws Exception {
ctrl = new NetconfControllerImpl();
ctrl.deviceFactory = new TestNetconfDeviceFactory();
ctrl.deviceService = deviceService;
ctrl.deviceKeyService = deviceKeyService;
//Creating mock devices
deviceInfo1 = new NetconfDeviceInfo("device1", "001", IpAddress.valueOf(DEVICE_1_IP), DEVICE_1_PORT);
deviceInfo2 = new NetconfDeviceInfo("device2", "002", IpAddress.valueOf(DEVICE_2_IP), DEVICE_2_PORT);
badDeviceInfo3 = new NetconfDeviceInfo("device3", "003", IpAddress.valueOf(BAD_DEVICE_IP), BAD_DEVICE_PORT);
deviceInfoIpV6 = new NetconfDeviceInfo("deviceIpv6", "004", IpAddress.valueOf(DEVICE_IPV6), IPV6_DEVICE_PORT);
device1 = new TestNetconfDevice(deviceInfo1);
deviceId1 = deviceInfo1.getDeviceId();
......@@ -102,9 +114,9 @@ public class NetconfControllerImplTest {
reflectedDownListener = (NetconfDeviceOutputEventListener) field2.get(ctrl);
eventForDeviceInfo1 = new NetconfDeviceOutputEvent(NetconfDeviceOutputEvent.Type.DEVICE_NOTIFICATION, null,
null, Optional.of(1), deviceInfo1);
null, Optional.of(1), deviceInfo1);
eventForDeviceInfo2 = new NetconfDeviceOutputEvent(NetconfDeviceOutputEvent.Type.DEVICE_UNREGISTERED, null,
null, Optional.of(2), deviceInfo2);
null, Optional.of(2), deviceInfo2);
}
@After
......@@ -128,7 +140,7 @@ public class NetconfControllerImplTest {
ctrl.addDeviceListener(deviceListener3);
assertThat("Incorrect number of listeners", ctrl.netconfDeviceListeners, hasSize(3));
assertThat("Not matching listeners", ctrl.netconfDeviceListeners, hasItems(deviceListener1,
deviceListener2, deviceListener3));
deviceListener2, deviceListener3));
ctrl.removeDeviceListener(deviceListener1);
assertThat("Incorrect number of listeners", ctrl.netconfDeviceListeners, hasSize(2));
......@@ -168,7 +180,7 @@ public class NetconfControllerImplTest {
public void testConnectBadDevice() throws Exception {
reflectedDeviceMap.clear();
try {
ctrl.connectDevice(badDeviceInfo3);
ctrl.connectDevice(badDeviceInfo3.getDeviceId());
} finally {
assertEquals("Incorrect device connection", 0, ctrl.getDevicesMap().size());
}
......@@ -180,25 +192,37 @@ public class NetconfControllerImplTest {
@Test
public void testConnectCorrectDevice() throws Exception {
reflectedDeviceMap.clear();
ctrl.connectDevice(deviceInfo1);
ctrl.connectDevice(deviceInfo2);
ctrl.connectDevice(deviceInfo1.getDeviceId());
ctrl.connectDevice(deviceInfo2.getDeviceId());
assertTrue("Incorrect device connection", ctrl.getDevicesMap().containsKey(deviceId1));
assertTrue("Incorrect device connection", ctrl.getDevicesMap().containsKey(deviceId2));
assertEquals("Incorrect device connection", 2, ctrl.getDevicesMap().size());
}
/**
* Check for correct ipv6 device connection. In this case the device map get modified.
*/
@Test
public void testConnectCorrectIpv6Device() throws Exception {
reflectedDeviceMap.clear();
ctrl.connectDevice(deviceInfoIpV6.getDeviceId());
assertTrue("Incorrect device connection", ctrl.getDevicesMap()
.containsKey(deviceInfoIpV6.getDeviceId()));
assertEquals("Incorrect device connection", 1, ctrl.getDevicesMap().size());
}
/**
* Check for connect devices already added to the map.
*/
@Test
public void testConnectAlreadyExistingDevice() throws Exception {
NetconfDevice alreadyExistingDevice1 = ctrl.connectDevice(deviceInfo1);
NetconfDevice alreadyExistingDevice2 = ctrl.connectDevice(deviceInfo2);
NetconfDevice alreadyExistingDevice1 = ctrl.connectDevice(deviceInfo1.getDeviceId());
NetconfDevice alreadyExistingDevice2 = ctrl.connectDevice(deviceInfo2.getDeviceId());
assertEquals("Incorrect device connection", alreadyExistingDevice1.getDeviceInfo().getDeviceId(),
deviceInfo1.getDeviceId());
deviceInfo1.getDeviceId());
assertEquals("Incorrect device connection", alreadyExistingDevice2.getDeviceInfo().getDeviceId(),
deviceInfo2.getDeviceId());
deviceInfo2.getDeviceId());
}
/**
......@@ -206,7 +230,7 @@ public class NetconfControllerImplTest {
*/
@Test
public void testDisconnectDevice() throws Exception {
ctrl.disconnectDevice(deviceInfo1);
ctrl.disconnectDevice(deviceInfo1.getDeviceId(), true);
assertFalse("Incorrect device removal", ctrl.getDevicesMap().containsKey(deviceId1));
}
......@@ -215,7 +239,7 @@ public class NetconfControllerImplTest {
*/
@Test
public void testRemoveDevice() throws Exception {
ctrl.removeDevice(deviceInfo1);
ctrl.removeDevice(deviceInfo1.getDeviceId());
assertFalse("Incorrect device removal", ctrl.getDevicesMap().containsKey(deviceId1));
}
......@@ -234,13 +258,13 @@ public class NetconfControllerImplTest {
@Test
public void testDeviceDownEventListener() throws Exception {
reflectedDeviceMap.clear();
ctrl.connectDevice(deviceInfo1);
ctrl.connectDevice(deviceInfo1.getDeviceId());
boolean result1 = reflectedDownListener.isRelevant(eventForDeviceInfo2);
assertFalse("Irrelevant Device Event", result1);
assertEquals("Incorrect device map size", 1, ctrl.getDevicesMap().size());
reflectedDownListener.event(eventForDeviceInfo1);
assertEquals("Incorrect device map size", 1, ctrl.getDevicesMap().size());
ctrl.connectDevice(deviceInfo2);
ctrl.connectDevice(deviceInfo2.getDeviceId());
boolean result2 = reflectedDownListener.isRelevant(eventForDeviceInfo2);
assertTrue("Irrelevant Device Event", result2);
assertEquals("Incorrect device map size", 2, ctrl.getDevicesMap().size());
......@@ -269,7 +293,7 @@ public class NetconfControllerImplTest {
public TestNetconfDevice(NetconfDeviceInfo deviceInfo) throws NetconfException {
netconfDeviceInfo = deviceInfo;
if (netconfDeviceInfo.ip() != badDeviceInfo3.ip()) {
if (!badDeviceInfo3.getDeviceId().equals(deviceInfo.getDeviceId())) {
netconfSession = EasyMock.createMock(NetconfSession.class);
deviceState = true;
} else {
......
/*
* Copyright 2016 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.netconf.ctl;
import org.onlab.packet.IpAddress;
import org.onosproject.net.key.DeviceKey;
import org.onosproject.net.key.DeviceKeyId;
import org.onosproject.net.key.DeviceKeyListener;
import org.onosproject.net.key.DeviceKeyService;
import org.onosproject.netconf.NetconfDeviceInfo;
import java.util.Collection;
/**
* Mock DeviceKey service to return device keys.
*/
class NetconfDeviceKeyServiceMock implements DeviceKeyService {
private static final String DEVICE_1_IP = "10.10.10.11";
private static final String DEVICE_2_IP = "10.10.10.12";
private static final String BAD_DEVICE_IP = "10.10.10.13";
private static final String DEVICE_IPV6 = "2001:db8::1";
private static final int DEVICE_1_PORT = 11;
private static final int DEVICE_2_PORT = 12;
private static final int BAD_DEVICE_PORT = 13;
private static final int IPV6_DEVICE_PORT = 14;
//DeviceInfo
private NetconfDeviceInfo deviceInfo1 =
new NetconfDeviceInfo("device1", "001", IpAddress.valueOf(DEVICE_1_IP),
DEVICE_1_PORT);
private NetconfDeviceInfo deviceInfo2 =
new NetconfDeviceInfo("device2", "002", IpAddress.valueOf(DEVICE_2_IP),
DEVICE_2_PORT);
private NetconfDeviceInfo badDeviceInfo3 =
new NetconfDeviceInfo("device3", "003", IpAddress.valueOf(BAD_DEVICE_IP),
BAD_DEVICE_PORT);
private NetconfDeviceInfo deviceInfoIpV6 =
new NetconfDeviceInfo("deviceIpv6", "004", IpAddress.valueOf(DEVICE_IPV6), IPV6_DEVICE_PORT);
@Override
public Collection<DeviceKey> getDeviceKeys() {
return null;
}
@Override
public DeviceKey getDeviceKey(DeviceKeyId deviceKeyId) {
if (deviceKeyId.toString().equals(deviceInfo1.getDeviceId().toString())) {
return DeviceKey.createDeviceKeyUsingUsernamePassword(
DeviceKeyId.deviceKeyId(deviceInfo1.getDeviceId().toString()),
null, deviceInfo1.name(), deviceInfo1.password());
} else if (deviceKeyId.toString().equals(deviceInfo2.getDeviceId().toString())) {
return DeviceKey.createDeviceKeyUsingUsernamePassword(
DeviceKeyId.deviceKeyId(deviceInfo2.getDeviceId().toString()),
null, deviceInfo2.name(), deviceInfo2.password());
} else if (deviceKeyId.toString().equals(badDeviceInfo3.getDeviceId().toString())) {
return DeviceKey.createDeviceKeyUsingUsernamePassword(
DeviceKeyId.deviceKeyId(badDeviceInfo3.getDeviceId().toString()),
null, badDeviceInfo3.name(), badDeviceInfo3.password());
} else if (deviceKeyId.toString().equals(deviceInfoIpV6.getDeviceId().toString())) {
return DeviceKey.createDeviceKeyUsingUsernamePassword(
DeviceKeyId.deviceKeyId(deviceInfoIpV6.getDeviceId().toString()),
null, deviceInfoIpV6.name(), deviceInfoIpV6.password());
}
return null;
}
@Override
public void addListener(DeviceKeyListener listener) {
}
@Override
public void removeListener(DeviceKeyListener listener) {
}
}
/*
* Copyright 2016 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.netconf.ctl;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.Port;
import org.onosproject.net.PortNumber;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.PortStatistics;
import java.util.List;
/**
* Mock device Service.
*/
class NetconfDeviceServiceMock implements DeviceService {
@Override
public int getDeviceCount() {
return 0;
}
@Override
public Iterable<Device> getDevices() {
return null;
}
@Override
public Iterable<Device> getDevices(Device.Type type) {
return null;
}
@Override
public Iterable<Device> getAvailableDevices() {
return null;
}
@Override
public Iterable<Device> getAvailableDevices(Device.Type type) {
return null;
}
@Override
public Device getDevice(DeviceId deviceId) {
return null;
}
@Override
public MastershipRole getRole(DeviceId deviceId) {
return null;
}
@Override
public List<Port> getPorts(DeviceId deviceId) {
return null;
}
@Override
public List<PortStatistics> getPortStatistics(DeviceId deviceId) {
return null;
}
@Override
public List<PortStatistics> getPortDeltaStatistics(DeviceId deviceId) {
return null;
}
@Override
public Port getPort(DeviceId deviceId, PortNumber portNumber) {
return null;
}
@Override
public boolean isAvailable(DeviceId deviceId) {
return false;
}
@Override
public void addListener(DeviceListener listener) {
}
@Override
public void removeListener(DeviceListener listener) {
}
}
\ No newline at end of file
......@@ -23,9 +23,12 @@ import org.apache.felix.scr.annotations.Deactivate;
import org.apache.felix.scr.annotations.Reference;
import org.apache.felix.scr.annotations.ReferenceCardinality;
import org.onlab.packet.ChassisId;
import org.onosproject.cluster.ClusterService;
import org.onosproject.cluster.NodeId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.incubator.net.config.basics.ConfigException;
import org.onosproject.mastership.MastershipService;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
......@@ -39,21 +42,27 @@ import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceEvent;
import org.onosproject.net.device.DeviceListener;
import org.onosproject.net.device.DeviceProvider;
import org.onosproject.net.device.DeviceProviderRegistry;
import org.onosproject.net.device.DeviceProviderService;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.key.DeviceKey;
import org.onosproject.net.key.DeviceKeyAdminService;
import org.onosproject.net.key.DeviceKeyId;
import org.onosproject.net.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.netconf.NetconfController;
import org.onosproject.netconf.NetconfDevice;
import org.onosproject.netconf.NetconfDeviceInfo;
import org.onosproject.netconf.NetconfDeviceListener;
import org.onosproject.netconf.NetconfException;
import org.slf4j.Logger;
import java.io.IOException;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
......@@ -67,6 +76,8 @@ import static org.slf4j.LoggerFactory.getLogger;
@Component(immediate = true)
public class NetconfDeviceProvider extends AbstractProvider
implements DeviceProvider {
public static final String ACTIVE = "active";
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
......@@ -82,21 +93,36 @@ public class NetconfDeviceProvider extends AbstractProvider
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DriverService driverService;
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected DeviceService deviceService;
protected DeviceKeyAdminService deviceKeyAdminService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected MastershipService mastershipService;
@Reference(cardinality = ReferenceCardinality.MANDATORY_UNARY)
protected ClusterService clusterService;
private static final String APP_NAME = "org.onosproject.netconf";
private static final String SCHEME_NAME = "netconf";
private static final String DEVICE_PROVIDER_PACKAGE = "org.onosproject.netconf.provider.device";
private static final String UNKNOWN = "unknown";
protected static final String ISNULL = "NetconfDeviceInfo is null";
private static final String IPADDRESS = "ipaddress";
private static final String NETCONF = "netconf";
private static final String PORT = "port";
//FIXME eventually a property
private static final int ISREACHABLE_TIMEOUT = 2000;
private final ExecutorService executor =
Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider", "device-installer-%d", log));
Executors.newFixedThreadPool(5, groupedThreads("onos/netconfdeviceprovider",
"device-installer-%d", log));
private DeviceProviderService providerService;
private NetconfDeviceListener innerNodeListener = new InnerNetconfDeviceListener();
private InternalDeviceListener deviceListener = new InternalDeviceListener();
private NodeId localNodeId;
private final ConfigFactory factory =
new ConfigFactory<ApplicationId, NetconfProviderConfig>(APP_SUBJECT_FACTORY,
......@@ -108,31 +134,40 @@ public class NetconfDeviceProvider extends AbstractProvider
return new NetconfProviderConfig();
}
};
private final NetworkConfigListener cfgLister = new InternalNetworkConfigListener();
private final NetworkConfigListener cfgListener = new InternalNetworkConfigListener();
private ApplicationId appId;
private boolean active;
@Activate
public void activate() {
active = true;
providerService = providerRegistry.register(this);
appId = coreService.registerApplication(APP_NAME);
cfgService.registerConfigFactory(factory);
cfgService.addListener(cfgLister);
cfgService.addListener(cfgListener);
controller.addDeviceListener(innerNodeListener);
deviceService.addListener(deviceListener);
executor.execute(NetconfDeviceProvider.this::connectDevices);
localNodeId = clusterService.getLocalNode().id();
log.info("Started");
}
@Deactivate
public void deactivate() {
deviceService.removeListener(deviceListener);
active = false;
controller.getNetconfDevices().forEach(id -> {
deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(id.toString()));
controller.disconnectDevice(id, true);
});
controller.removeDeviceListener(innerNodeListener);
controller.getNetconfDevices().forEach(id ->
controller.removeDevice(controller.getDevicesMap().get(id)
.getDeviceInfo()));
deviceService.removeListener(deviceListener);
providerRegistry.unregister(this);
providerService = null;
cfgService.unregisterConfigFactory(factory);
executor.shutdown();
log.info("Stopped");
}
......@@ -148,52 +183,87 @@ public class NetconfDeviceProvider extends AbstractProvider
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
// TODO: This will be implemented later.
if (active) {
switch (newRole) {
case MASTER:
initiateConnection(deviceId, newRole);
log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
break;
case STANDBY:
controller.disconnectDevice(deviceId, false);
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
//else no-op
break;
case NONE:
controller.disconnectDevice(deviceId, false);
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
break;
default:
log.error("Unimplemented Mastership state : {}", newRole);
}
}
}
@Override
public boolean isReachable(DeviceId deviceId) {
NetconfDevice netconfDevice = controller.getNetconfDevice(deviceId);
if (netconfDevice == null) {
log.debug("Requested device id: {} is not associated to any " +
"NETCONF Device", deviceId.toString());
//FIXME this is a workaround util device state is shared
// between controller instances.
Device device = deviceService.getDevice(deviceId);
String ip;
int port;
Socket socket = null;
if (device != null) {
ip = device.annotations().value(IPADDRESS);
port = Integer.parseInt(device.annotations().value(PORT));
} else {
String[] info = deviceId.toString().split(":");
if (info.length == 3) {
ip = info[1];
port = Integer.parseInt(info[2]);
} else {
ip = Arrays.asList(info).stream().filter(el -> !el.equals(info[0])
&& !el.equals(info[info.length - 1]))
.reduce((t, u) -> t + ":" + u)
.get();
log.debug("ip v6 {}", ip);
port = Integer.parseInt(info[info.length - 1]);
}
}
//test connection to device opening a socket to it.
try {
socket = new Socket(ip, port);
log.debug("rechability of {}, {}, {}", deviceId, socket.isConnected() && !socket.isClosed());
return socket.isConnected() && !socket.isClosed();
} catch (IOException e) {
log.info("Device {} is not reachable", deviceId);
return false;
} finally {
if (socket != null) {
try {
socket.close();
} catch (IOException e) {
log.debug("Test Socket failed {} ", deviceId);
return false;
}
}
}
return netconfDevice.isActive();
}
private class InnerNetconfDeviceListener implements NetconfDeviceListener {
private static final String IPADDRESS = "ipaddress";
protected static final String ISNULL = "NetconfDeviceInfo is null";
@Override
public void deviceAdded(NetconfDeviceInfo nodeId) {
Preconditions.checkNotNull(nodeId, ISNULL);
DeviceId deviceId = nodeId.getDeviceId();
//Netconf configuration object
ChassisId cid = new ChassisId();
String ipAddress = nodeId.ip().toString();
SparseAnnotations annotations = DefaultAnnotations.builder()
.set(IPADDRESS, ipAddress)
.set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
.build();
DeviceDescription deviceDescription = new DefaultDeviceDescription(
deviceId.uri(),
Device.Type.SWITCH,
UNKNOWN, UNKNOWN,
UNKNOWN, UNKNOWN,
cid,
annotations);
providerService.deviceConnected(deviceId, deviceDescription);
public void deviceAdded(DeviceId deviceId) {
//no-op
log.debug("Netconf device {} added to Netconf subController", deviceId);
}
@Override
public void deviceRemoved(NetconfDeviceInfo nodeId) {
Preconditions.checkNotNull(nodeId, ISNULL);
DeviceId deviceId = nodeId.getDeviceId();
public void deviceRemoved(DeviceId deviceId) {
Preconditions.checkNotNull(deviceId, ISNULL);
log.debug("Netconf device {} removed from Netconf subController", deviceId);
providerService.deviceDisconnected(deviceId);
}
}
......@@ -201,41 +271,85 @@ public class NetconfDeviceProvider extends AbstractProvider
NetconfProviderConfig cfg = cfgService.getConfig(appId, NetconfProviderConfig.class);
if (cfg != null) {
try {
cfg.getDevicesAddresses().stream()
.forEach(addr -> {
try {
NetconfDeviceInfo netconf = new NetconfDeviceInfo(addr.name(),
addr.password(),
addr.ip(),
addr.port());
controller.connectDevice(netconf);
Device device = deviceService.getDevice(netconf.getDeviceId());
if (device.is(PortDiscovery.class)) {
PortDiscovery portConfig = device.as(PortDiscovery.class);
if (portConfig != null) {
providerService.updatePorts(netconf.getDeviceId(),
portConfig.getPorts());
}
} else {
log.warn("No portGetter behaviour for device {}", netconf.getDeviceId());
}
} catch (IOException e) {
throw new RuntimeException(
new NetconfException(
"Can't connect to NETCONF " +
"device on " + addr.ip() +
":" + addr.port(), e));
}
}
);
cfg.getDevicesAddresses().stream().forEach(addr -> {
DeviceId deviceId = getDeviceId(addr.ip().toString(), addr.port());
Preconditions.checkNotNull(deviceId, ISNULL);
//Netconf configuration object
ChassisId cid = new ChassisId();
String ipAddress = addr.ip().toString();
SparseAnnotations annotations = DefaultAnnotations.builder()
.set(IPADDRESS, ipAddress)
.set(PORT, String.valueOf(addr.port()))
.set(AnnotationKeys.PROTOCOL, SCHEME_NAME.toUpperCase())
.build();
DeviceDescription deviceDescription = new DefaultDeviceDescription(
deviceId.uri(),
Device.Type.SWITCH,
UNKNOWN, UNKNOWN,
UNKNOWN, UNKNOWN,
cid,
annotations);
deviceKeyAdminService.addKey(
DeviceKey.createDeviceKeyUsingUsernamePassword(
DeviceKeyId.deviceKeyId(deviceId.toString()),
null, addr.name(), addr.password()));
providerService.deviceConnected(deviceId, deviceDescription);
});
} catch (ConfigException e) {
log.error("Cannot read config error " + e);
}
}
}
private void initiateConnection(DeviceId deviceId, MastershipRole newRole) {
try {
if (isReachable(deviceId)) {
controller.connectDevice(deviceId);
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
} else {
return;
}
} catch (Exception e) {
if (deviceService.getDevice(deviceId) != null) {
providerService.deviceDisconnected(deviceId);
}
deviceKeyAdminService.removeKey(DeviceKeyId.deviceKeyId(deviceId.toString()));
throw new RuntimeException(new NetconfException(
"Can't connect to NETCONF " + "device on " + deviceId + ":" + deviceId, e));
}
}
private void discoverPorts(DeviceId deviceId) {
Device device = deviceService.getDevice(deviceId);
if (device.is(PortDiscovery.class)) {
PortDiscovery portConfig = device.as(PortDiscovery.class);
providerService.updatePorts(deviceId,
portConfig.getPorts());
} else {
log.warn("No portGetter behaviour for device {}", deviceId);
}
}
/**
* Return the DeviceId about the device containing the URI.
*
* @return DeviceId
*/
public DeviceId getDeviceId(String ip, int port) {
try {
return DeviceId.deviceId(new URI(NETCONF, ip + ":" + port, null));
} catch (URISyntaxException e) {
throw new IllegalArgumentException("Unable to build deviceID for device "
+ ip + ":" + port, e);
}
}
/**
* Listener for configuration events.
*/
private class InternalNetworkConfigListener implements NetworkConfigListener {
......@@ -246,10 +360,35 @@ public class NetconfDeviceProvider extends AbstractProvider
@Override
public boolean isRelevant(NetworkConfigEvent event) {
//TODO refactor
return event.configClass().equals(NetconfProviderConfig.class) &&
(event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED);
}
}
/**
* Listener for core device events.
*/
private class InternalDeviceListener implements DeviceListener {
@Override
public void event(DeviceEvent event) {
if ((event.type() == DeviceEvent.Type.DEVICE_ADDED)) {
executor.execute(() -> discoverPorts(event.subject().id()));
} else if ((event.type() == DeviceEvent.Type.DEVICE_REMOVED)) {
log.debug("removing device {}", event.subject().id());
deviceService.getDevice(event.subject().id()).annotations().keys();
controller.disconnectDevice(event.subject().id(), true);
}
}
@Override
public boolean isRelevant(DeviceEvent event) {
if (mastershipService.getMasterFor(event.subject().id()) == null) {
return true;
}
return event.subject().annotations().value(AnnotationKeys.PROTOCOL)
.equals(SCHEME_NAME.toUpperCase()) &&
mastershipService.getMasterFor(event.subject().id()).equals(localNodeId);
}
}
}
......
......@@ -8,4 +8,4 @@ export OCN="10.128.12.4"
export OCT=$OC1
export ONOS_USE_SSH=true
export ONOS_APPS=drivers,openflow,proxyarp,ovsdb,pathpainter
export ONOS_APPS=drivers,openflow,proxyarp,ovsdb,pathpainter
\ No newline at end of file
......