blob: 23f7ad26b5923a180e3b23ad4eafc91a3a3cb80b [file] [log] [blame]
/*
* Copyright 2016-present Open Networking Foundation
*
* 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.provider.tl1.device.impl;
import org.osgi.service.component.annotations.Activate;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Deactivate;
import org.osgi.service.component.annotations.Reference;
import org.osgi.service.component.annotations.ReferenceCardinality;
import org.onlab.packet.ChassisId;
import org.onosproject.core.ApplicationId;
import org.onosproject.core.CoreService;
import org.onosproject.net.AnnotationKeys;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.MastershipRole;
import org.onosproject.net.PortNumber;
import org.onosproject.net.SparseAnnotations;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.NetworkConfigRegistry;
import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.net.device.DefaultDeviceDescription;
import org.onosproject.net.device.DeviceAdminService;
import org.onosproject.net.device.DeviceDescription;
import org.onosproject.net.device.DeviceDescriptionDiscovery;
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.provider.AbstractProvider;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.tl1.DefaultTl1Device;
import org.onosproject.tl1.Tl1Controller;
import org.onosproject.tl1.Tl1Device;
import org.onosproject.tl1.Tl1Listener;
import org.onosproject.tl1.device.Tl1DeviceConfig;
import org.slf4j.Logger;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.NoSuchElementException;
import java.util.Set;
import static org.slf4j.LoggerFactory.getLogger;
/**
* Device provider for TL1 devices.
* <p>
* Sits between ONOS provider service and the TL1 controller.
* Relies on network config subsystem to know about devices.
*/
@Component(immediate = true)
public class Tl1DeviceProvider extends AbstractProvider implements DeviceProvider {
private static final String APP_NAME = "org.onosproject.tl1";
private static final String PROVIDER = "org.onosproject.provider.tl1.device";
private static final String UNKNOWN = "unknown";
private static final int REACHABILITY_TIMEOUT = 2000; // in milliseconds
private final Logger log = getLogger(getClass());
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected NetworkConfigRegistry cfgRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected CoreService coreService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceProviderRegistry providerRegistry;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceAdminService deviceAdminService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected DeviceService deviceService;
@Reference(cardinality = ReferenceCardinality.MANDATORY)
protected Tl1Controller controller;
private ApplicationId appId;
private NetworkConfigListener cfgListener = new InnerConfigListener();
private Tl1Listener tl1Listener = new InnerTl1Listener();
private DeviceProviderService providerService;
private final ConfigFactory factory =
new ConfigFactory<DeviceId, Tl1DeviceConfig>(SubjectFactories.DEVICE_SUBJECT_FACTORY,
Tl1DeviceConfig.class,
Tl1DeviceConfig.TL1) {
@Override
public Tl1DeviceConfig createConfig() {
return new Tl1DeviceConfig();
}
};
@Activate
public void activate() {
appId = coreService.registerApplication(APP_NAME);
providerService = providerRegistry.register(this);
cfgRegistry.addListener(cfgListener);
controller.addListener(tl1Listener);
cfgRegistry.registerConfigFactory(factory);
connectDevices();
log.info("Started");
}
@Deactivate
public void deactivate() {
controller.removeListener(tl1Listener);
cfgRegistry.removeListener(cfgListener);
controller.getDeviceIds().forEach(deviceId -> {
controller.removeDevice(deviceId);
deviceAdminService.removeDevice(deviceId);
});
providerRegistry.unregister(this);
cfgRegistry.unregisterConfigFactory(factory);
providerService = null;
log.info("Stopped");
}
public Tl1DeviceProvider() {
super(new ProviderId(Tl1DeviceConfig.TL1, PROVIDER));
}
@Override
public void triggerProbe(DeviceId deviceId) {
// TODO
}
@Override
public void roleChanged(DeviceId deviceId, MastershipRole newRole) {
switch (newRole) {
case MASTER:
controller.connectDevice(deviceId);
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.MASTER);
log.debug("Accepting mastership role change to {} for device {}", newRole, deviceId);
break;
case STANDBY:
controller.disconnectDevice(deviceId);
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.STANDBY);
break;
case NONE:
controller.disconnectDevice(deviceId);
providerService.receivedRoleReply(deviceId, newRole, MastershipRole.NONE);
break;
default:
log.error("Invalid mastership state: {}", newRole);
}
}
// Assumes device is registered in TL1 controller.
@Override
public boolean isReachable(DeviceId deviceId) {
try {
// First check if device is already connected.
// If not, try to open a socket.
Tl1Device device = controller.getDevice(deviceId).get();
if (device.isConnected()) {
return true;
}
Socket socket = new Socket();
socket.connect(new InetSocketAddress(device.ip().toInetAddress(), device.port()), REACHABILITY_TIMEOUT);
socket.close();
return true;
} catch (NoSuchElementException | IOException | IllegalArgumentException e) {
log.error("Cannot reach device {}", deviceId, e);
return false;
}
}
@Override
public void changePortState(DeviceId deviceId, PortNumber portNumber, boolean enable) {
// TODO
}
//Method to register devices provided via net-cfg under devices/ tree
private void connectDevices() {
Set<DeviceId> deviceSubjects =
cfgRegistry.getSubjects(DeviceId.class, Tl1DeviceConfig.class);
deviceSubjects.forEach(deviceId -> {
Tl1DeviceConfig config =
cfgRegistry.getConfig(deviceId, Tl1DeviceConfig.class);
connectDevice(new DefaultTl1Device(config.ip(), config.port(), config.username(),
config.password()));
});
}
// Register a device in the core and in the TL1 controller.
private void connectDevice(Tl1Device device) {
try {
// Add device to TL1 controller
DeviceId deviceId = DeviceId.deviceId(
new URI(Tl1DeviceConfig.TL1, device.ip() + ":" + device.port(), null));
if (controller.addDevice(deviceId, device)) {
SparseAnnotations ann = DefaultAnnotations.builder()
.set(AnnotationKeys.PROTOCOL, Tl1DeviceConfig.TL1.toUpperCase())
.build();
// Register device in the core with default parameters and mark it as unavailable
DeviceDescription dd = new DefaultDeviceDescription(deviceId.uri(),
Device.Type.SWITCH,
UNKNOWN, UNKNOWN,
UNKNOWN, UNKNOWN,
new ChassisId(),
false, ann);
providerService.deviceConnected(deviceId, dd);
}
} catch (URISyntaxException e) {
log.error("Skipping device {}", device, e);
}
}
/**
* Tries to update the device and port descriptions through the {@code DeviceDescriptionDiscovery} behaviour.
*
* @param deviceId the device
*/
void updateDevice(DeviceId deviceId) {
Device device = deviceService.getDevice(deviceId);
if (!device.is(DeviceDescriptionDiscovery.class)) {
return;
}
try {
// Update device description
DeviceDescriptionDiscovery discovery = device.as(DeviceDescriptionDiscovery.class);
DeviceDescription dd = discovery.discoverDeviceDetails();
if (dd == null) {
return;
}
providerService.deviceConnected(deviceId,
new DefaultDeviceDescription(dd, true, dd.annotations()));
// Update ports
providerService.updatePorts(deviceId, discovery.discoverPortDetails());
} catch (IllegalStateException | IllegalArgumentException e) {
log.error("Cannot update device description {}", deviceId, e);
}
}
/**
* Listener for network configuration events.
*/
private class InnerConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
if (event.type() == NetworkConfigEvent.Type.CONFIG_ADDED) {
if (event.configClass().equals(Tl1DeviceConfig.class)) {
connectDevices();
} else {
log.warn("Injecting device via this Json is deprecated, " +
"please put configuration under devices/");
}
} else if (event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED) {
// TODO: calculate delta
if (event.configClass().equals(Tl1DeviceConfig.class)) {
connectDevices();
} else {
log.warn("Injecting device via this Json is deprecated, " +
"please put configuration under devices/");
}
} else if (event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED) {
controller.getDeviceIds().forEach(deviceId -> {
controller.removeDevice(deviceId);
deviceAdminService.removeDevice(deviceId);
});
}
}
@Override
public boolean isRelevant(NetworkConfigEvent event) {
return (event.configClass().equals(Tl1DeviceConfig.class)) &&
(event.type() == NetworkConfigEvent.Type.CONFIG_ADDED ||
event.type() == NetworkConfigEvent.Type.CONFIG_UPDATED ||
event.type() == NetworkConfigEvent.Type.CONFIG_REMOVED);
}
}
/**
* Listener for TL1 events.
*/
private class InnerTl1Listener implements Tl1Listener {
@Override
public void deviceConnected(DeviceId deviceId) {
updateDevice(deviceId);
}
@Override
public void deviceDisconnected(DeviceId deviceId) {
providerService.deviceDisconnected(deviceId);
}
}
}