Thomas Vachuska
Committed by Gerrit Code Review

Adding ability to project device, link and host model as alternate entities.

Change-Id: If23c018b024a3bbe693f0e66888c5f1707e3f66d
package org.onosproject.flowanalyzer;
import org.onosproject.net.PortNumber;
import org.onosproject.net.link.LinkServiceAdapter;
import org.onosproject.net.topology.TopologyEdge;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.DefaultLink;
import org.onosproject.net.DeviceId;
import org.onosproject.net.ElementId;
import org.onosproject.net.HostId;
import org.onosproject.net.Link;
import org.onosproject.net.Annotations;
import org.onosproject.net.provider.ProviderId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.link.LinkServiceAdapter;
import org.onosproject.net.topology.TopologyEdge;
import org.onosproject.net.topology.TopologyVertex;
import java.util.Set;
import java.util.ArrayList;
import java.util.List;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static org.onosproject.net.Link.State.ACTIVE;
/**
* Created by nikcheerla on 7/21/15.
* Test fixture for the flow analyzer.
*/
public class MockLinkService extends LinkServiceAdapter {
DefaultMutableTopologyGraph createdGraph = new DefaultMutableTopologyGraph(new HashSet<>(), new HashSet<>());
......@@ -47,7 +45,7 @@ public class MockLinkService extends LinkServiceAdapter {
@Override
public Set<Link> getDeviceEgressLinks(DeviceId deviceId) {
Set<Link> setL = new HashSet<>();
for (Link l: links) {
for (Link l : links) {
if (l.src().elementId() instanceof DeviceId && l.src().deviceId().equals(deviceId)) {
setL.add(l);
}
......@@ -58,7 +56,7 @@ public class MockLinkService extends LinkServiceAdapter {
@Override
public Set<Link> getDeviceIngressLinks(DeviceId deviceId) {
Set<Link> setL = new HashSet<>();
for (Link l: links) {
for (Link l : links) {
if (l.dst().elementId() instanceof DeviceId && l.dst().deviceId().equals(deviceId)) {
setL.add(l);
}
......@@ -70,7 +68,7 @@ public class MockLinkService extends LinkServiceAdapter {
@Override
public Set<Link> getEgressLinks(ConnectPoint pt) {
Set<Link> setL = new HashSet<>();
for (Link l: links) {
for (Link l : links) {
if (l.src().equals(pt)) {
setL.add(l);
}
......@@ -81,7 +79,7 @@ public class MockLinkService extends LinkServiceAdapter {
@Override
public Set<Link> getIngressLinks(ConnectPoint pt) {
Set<Link> setL = new HashSet<>();
for (Link l: links) {
for (Link l : links) {
if (l.dst().equals(pt)) {
setL.add(l);
}
......@@ -92,7 +90,7 @@ public class MockLinkService extends LinkServiceAdapter {
@Override
public Set<Link> getLinks(ConnectPoint pt) {
Set<Link> setL = new HashSet<>();
for (Link l: links) {
for (Link l : links) {
if (l.src().equals(pt) || l.dst().equals(pt)) {
setL.add(l);
}
......@@ -119,47 +117,7 @@ public class MockLinkService extends LinkServiceAdapter {
ConnectPoint src = new ConnectPoint(d1, PortNumber.portNumber(port));
ConnectPoint dst = new ConnectPoint(d2, PortNumber.portNumber(port2));
Link curLink;
curLink = new Link() {
@Override
public ConnectPoint src() {
return src;
}
@Override
public ConnectPoint dst() {
return dst;
}
@Override
public boolean isDurable() {
return true;
}
@Override
public boolean isExpected() {
return false;
}
@Override
public Annotations annotations() {
return null;
}
@Override
public Type type() {
return null;
}
@Override
public ProviderId providerId() {
return null;
}
@Override
public State state() {
return ACTIVE;
}
};
curLink = DefaultLink.builder().src(src).dst(dst).state(ACTIVE).build();
links.add(curLink);
if (d1 instanceof DeviceId && d2 instanceof DeviceId) {
TopologyVertex v1 = () -> (DeviceId) d1, v2 = () -> (DeviceId) d2;
......
......@@ -20,7 +20,7 @@ import org.onosproject.net.provider.ProviderId;
/**
* Base implementation of network elements, i.e. devices or hosts.
*/
public abstract class AbstractElement extends AbstractModel implements Element {
public abstract class AbstractElement extends AbstractProjectableModel implements Element {
protected final ElementId id;
......
/*
* Copyright 2014-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.net;
import com.google.common.annotations.Beta;
import org.onlab.util.ItemNotFoundException;
import org.onosproject.net.driver.Behaviour;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.driver.DriverData;
import org.onosproject.net.driver.DriverService;
import org.onosproject.net.driver.Projectable;
import org.onosproject.net.provider.ProviderId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.Set;
import static com.google.common.base.Preconditions.checkState;
/**
* Base model entity, capable of being extended via projection mechanism.
*/
@Beta
public abstract class AbstractProjectableModel extends AbstractModel implements Projectable {
private static Logger log = LoggerFactory.getLogger(AbstractProjectableModel.class);
protected static final String NO_DRIVER_SERVICE = "Driver service not bound yet";
protected static final String NO_DRIVER = "Driver has not been bound to %s";
// Static reference to the driver service; injected via setDriverService
private static DriverService driverService;
private Driver driver;
// For serialization
public AbstractProjectableModel() {
}
/**
* Creates a model entity attributed to the specified provider and
* optionally annotated.
*
* @param providerId identity of the provider
* @param annotations optional key/value annotations
*/
public AbstractProjectableModel(ProviderId providerId, Annotations[] annotations) {
super(providerId, annotations);
}
/**
* Injects the driver service reference for use during projections into
* various behaviours.
* <p>
* This is a privileged call; unauthorized invocations will result in
* illegal state exception
*
* @param key opaque admin key object
* @param driverService injected driver service
* @throws IllegalStateException when invoked sans authorization
*/
public static void setDriverService(Object key, DriverService driverService) {
// TODO: Rework this once we have means to enforce access to admin services in general
checkState(AbstractProjectableModel.driverService == key, "Unauthorized invocation");
AbstractProjectableModel.driverService = driverService;
}
/**
* Returns the currently bound driver service reference.
*
* @return driver service
*/
protected static DriverService driverService() {
return driverService;
}
/**
* Returns the currently bound driver or null of no driver is bound.
*
* @return bound driver; null if none
*/
protected Driver driver() {
return driver;
}
@Override
public <B extends Behaviour> B as(Class<B> projectionClass) {
checkState(driverService != null, NO_DRIVER_SERVICE);
if (driver == null) {
driver = locateDriver();
}
checkState(driver != null, NO_DRIVER, this);
return driver.createBehaviour(asData(), projectionClass);
}
@Override
public <B extends Behaviour> boolean is(Class<B> projectionClass) {
checkState(driverService != null, NO_DRIVER_SERVICE);
if (driver == null) {
driver = locateDriver();
}
checkState(driver != null, "Driver has not been bound to %s", this);
return driver.hasBehaviour(projectionClass);
}
/**
* Locates the driver to be used by this entity.
* <p>
* The default implementation derives the driver based on the {@code driver}
* annotation value.
*
* @return driver for alternate projections of this model entity or null
* if no driver is expected or driver is not found
*/
protected Driver locateDriver() {
String driverName = annotations().value(AnnotationKeys.DRIVER);
if (driverName != null) {
try {
return driverService.getDriver(driverName);
} catch (ItemNotFoundException e) {
log.warn("Driver {} not found.", driverName);
}
}
return null;
}
/**
* Returns self as an immutable driver data instance.
*
* @return self as driver data
*/
protected DriverData asData() {
return new AnnotationDriverData();
}
/**
* Projection of the parent entity as a driver data entity.
*/
protected class AnnotationDriverData implements DriverData {
@Override
public Driver driver() {
return driver;
}
@Override
public DeviceId deviceId() {
throw new UnsupportedOperationException("Entity not a device");
}
@Override
public MutableAnnotations set(String key, String value) {
throw new UnsupportedOperationException("Entity is immutable");
}
@Override
public MutableAnnotations clear(String... keys) {
throw new UnsupportedOperationException("Entity is immutable");
}
@Override
public Set<String> keys() {
return annotations().keys();
}
@Override
public String value(String key) {
return annotations().value(key);
}
}
}
......@@ -15,12 +15,15 @@
*/
package org.onosproject.net;
import org.onosproject.net.provider.ProviderId;
import org.onlab.packet.ChassisId;
import org.onosproject.net.driver.Driver;
import org.onosproject.net.driver.DriverData;
import org.onosproject.net.provider.ProviderId;
import java.util.Objects;
import static com.google.common.base.MoreObjects.toStringHelper;
import static org.onlab.util.Tools.nullIsNotFound;
/**
* Default infrastructure device model implementation.
......@@ -105,6 +108,33 @@ public class DefaultDevice extends AbstractElement implements Device {
return chassisId;
}
/**
* Returns self as an immutable driver data instance.
*
* @return self as driver data
*/
protected DriverData asData() {
return new DeviceDriverData();
}
@Override
protected Driver locateDriver() {
Driver driver = super.locateDriver();
return driver != null ? driver :
nullIsNotFound(driverService().getDriver(manufacturer, hwVersion, swVersion),
"Driver not found");
}
/**
* Projection of the parent entity as a driver data entity.
*/
protected class DeviceDriverData extends AnnotationDriverData {
@Override
public DeviceId deviceId() {
return id();
}
}
@Override
public int hashCode() {
return Objects.hash(id, type, manufacturer, hwVersion, swVersion, serialNumber);
......@@ -136,6 +166,7 @@ public class DefaultDevice extends AbstractElement implements Device {
.add("hwVersion", hwVersion)
.add("swVersion", swVersion)
.add("serialNumber", serialNumber)
.add("driver", driver() != null ? driver().name() : "")
.toString();
}
......
......@@ -24,11 +24,10 @@ import static org.onosproject.net.Link.State.ACTIVE;
import static org.onosproject.net.DefaultAnnotations.EMPTY;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Default infrastructure link model implementation.
*/
public class DefaultLink extends AbstractModel implements Link {
public class DefaultLink extends AbstractProjectableModel implements Link {
private final ConnectPoint src;
private final ConnectPoint dst;
......@@ -60,7 +59,7 @@ public class DefaultLink extends AbstractModel implements Link {
* @param dst link destination
* @param type link type
* @param state link state
* @param isExpected indicates if the link is preconfigured
* @param isExpected indicates if the link is preconfigured
* @param annotations optional key/value annotations
*/
private DefaultLink(ProviderId providerId, ConnectPoint src, ConnectPoint dst,
......
......@@ -15,10 +15,12 @@
*/
package org.onosproject.net;
import org.onosproject.net.driver.Projectable;
/**
* Base abstraction of a network element, i.e. an infrastructure device or an end-station host.
*/
public interface Element extends Annotated, Provided {
public interface Element extends Annotated, Provided, Projectable {
/**
* Returns the network element identifier.
......
......@@ -15,10 +15,12 @@
*/
package org.onosproject.net;
import org.onosproject.net.driver.Projectable;
/**
* Abstraction of a network infrastructure link.
*/
public interface Link extends Annotated, Provided, NetworkResource {
public interface Link extends Annotated, Provided, Projectable, NetworkResource {
/**
* Coarse representation of the link type.
......
......@@ -18,7 +18,7 @@ package org.onosproject.net.driver;
import static com.google.common.base.Preconditions.checkState;
/**
* Base implementation of device driver behaviour.
* Base implementation of a driver behaviour.
*/
public class AbstractBehaviour implements Behaviour {
......
......@@ -18,7 +18,7 @@ package org.onosproject.net.driver;
import static com.google.common.base.Preconditions.checkState;
/**
* Base implementation of device driver handler behaviour.
* Base implementation of a driver handler behaviour.
*/
public class AbstractHandlerBehaviour
extends AbstractBehaviour implements HandlerBehaviour {
......
......@@ -16,8 +16,8 @@
package org.onosproject.net.driver;
/**
* Representation of a facet of device behaviour that can be used to talk about
* a device (in context of {@link DriverData}) or to a device (in context of
* Representation of a facet of behaviour that can be used to talk about
* an entity (in context of {@link DriverData}) or to an entity (in context of
* {@link DriverHandler}).
*/
public interface Behaviour {
......
......@@ -57,11 +57,6 @@ public class DefaultDriverData implements DriverData {
}
@Override
public <T extends Behaviour> T behaviour(Class<T> behaviourClass) {
return driver.createBehaviour(this, behaviourClass);
}
@Override
public MutableAnnotations set(String key, String value) {
properties.put(key, value);
return this;
......
......@@ -19,7 +19,7 @@ import org.onosproject.net.DeviceId;
import org.onosproject.net.MutableAnnotations;
/**
* Container for data about a device. Data is stored using
* Container for data about an entity, e.g. device, link. Data is stored using
* {@link org.onosproject.net.MutableAnnotations}.
*
* Note that only derivatives of {@link HandlerBehaviour} can expect mutability
......@@ -45,10 +45,15 @@ public interface DriverData extends MutableAnnotations {
/**
* Returns the specified facet of behaviour to access the device data.
*
* Implementations are expected to defer to the backing driver for creation
* of the requested behaviour.
*
* @param behaviourClass behaviour class
* @param <T> type of behaviour
* @return requested behaviour or null if not supported
*/
<T extends Behaviour> T behaviour(Class<T> behaviourClass);
default <T extends Behaviour> T behaviour(Class<T> behaviourClass) {
return driver().createBehaviour(this, behaviourClass);
}
}
......
......@@ -16,8 +16,8 @@
package org.onosproject.net.driver;
/**
* Representation of a facet of device behaviour that can be used to interact
* with a device (in context of {@link org.onosproject.net.driver.DriverHandler}).
* Representation of a facet of behaviour that can be used to interact
* with an entity (in context of {@link org.onosproject.net.driver.DriverHandler}).
*/
public interface HandlerBehaviour extends Behaviour {
......
......@@ -38,7 +38,7 @@ public interface Projectable {
* Returns true if this entity is capable of being projected as the
* specified class.
*
* @param projectionClass projection class
* @param projectionClass requested projection class
* @param <B> type of behaviour
* @return true if the requested projection is supported
*/
......
......@@ -16,21 +16,23 @@
/**
* Set of facilities to allow the platform to be extended with
* device specific behaviours and to allow modeling device behaviours while
* hiding details of specific device driver implementations.
* device specific behaviours and to allow modeling device (and other entity)
* behaviours while hiding details of specific driver implementations.
* While primarily intended for devices, this subsystem can be used to abstract
* behaviours of other entities as well.
* <p>
* {@link org.onosproject.net.driver.Driver} is a representation of a
* specific family of devices supports set of
* specific family of entities (devices, links, etc.) which supports set of
* {@link org.onosproject.net.driver.Behaviour behaviour classes}. Default
* implementation is provided by the platform and allows DriverProviders to
* add different behaviour implementations via DriverService.
* </p>
* <p>
* {@link org.onosproject.net.driver.DriverData} is a container for data
* learned about a device. It is associated with a specific
* learned about an entity. It is associated with a specific
* {@link org.onosproject.net.driver.Driver}
* and provides set of {@link org.onosproject.net.driver.Behaviour behaviours}
* for talking about a device. A default
* for talking about an entity. A default
* implementation provided by platform and has mutable key/value store for use by
* implementations of {@link org.onosproject.net.driver.Behaviour behaviours}.
* </p>
......
......@@ -22,6 +22,7 @@ import org.onosproject.net.DeviceId;
import org.onosproject.net.Link;
import org.onosproject.net.LinkKey;
import org.onosproject.net.PortNumber;
import org.onosproject.net.driver.Behaviour;
import org.onosproject.net.provider.ProviderId;
/**
......@@ -63,6 +64,16 @@ public abstract class BiLinkTestBase {
@Override public ProviderId providerId() {
return null;
}
@Override
public <B extends Behaviour> B as(Class<B> projectionClass) {
return null;
}
@Override
public <B extends Behaviour> boolean is(Class<B> projectionClass) {
return false;
}
}
protected static final DeviceId DEV_A_ID = DeviceId.deviceId("device-A");
......
......@@ -19,24 +19,18 @@ package org.onosproject.ui.topo;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.google.common.collect.ImmutableSet;
import org.junit.Test;
import org.onlab.packet.ChassisId;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onlab.packet.VlanId;
import org.onosproject.net.Annotations;
import org.onosproject.net.DefaultDevice;
import org.onosproject.net.DefaultHost;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.HostId;
import org.onosproject.net.HostLocation;
import org.onosproject.net.device.DeviceService;
import org.onosproject.net.device.DeviceServiceAdapter;
import org.onosproject.net.host.HostService;
import org.onosproject.net.host.HostServiceAdapter;
import org.onosproject.net.provider.ProviderId;
import java.util.Set;
import static org.junit.Assert.*;
......@@ -45,106 +39,18 @@ import static org.junit.Assert.*;
*/
public class NodeSelectionTest {
private static class FakeDevice implements Device {
private final DeviceId id;
private static class FakeDevice extends DefaultDevice {
FakeDevice(DeviceId id) {
this.id = id;
}
@Override
public DeviceId id() {
return id;
}
@Override
public Type type() {
return null;
}
@Override
public String manufacturer() {
return null;
}
@Override
public String hwVersion() {
return null;
}
@Override
public String swVersion() {
return null;
}
@Override
public String serialNumber() {
return null;
}
@Override
public ChassisId chassisId() {
return null;
}
@Override
public Annotations annotations() {
return null;
}
@Override
public ProviderId providerId() {
return null;
super(null, id, null, null, null, null, null, null);
}
}
private static class FakeHost implements Host {
private final HostId id;
private static class FakeHost extends DefaultHost {
FakeHost(HostId id) {
this.id = id;
}
@Override
public HostId id() {
return id;
}
@Override
public MacAddress mac() {
return null;
}
@Override
public VlanId vlan() {
return null;
}
@Override
public Set<IpAddress> ipAddresses() {
return null;
}
@Override
public HostLocation location() {
return null;
}
@Override
public Annotations annotations() {
return null;
}
@Override
public ProviderId providerId() {
return null;
super(null, id, null, null, null, ImmutableSet.of());
}
}
private final ObjectMapper mapper = new ObjectMapper();
private static final String IDS = "ids";
......
......@@ -21,6 +21,7 @@ import org.onlab.packet.ChassisId;
import org.onosproject.codec.CodecContext;
import org.onosproject.net.Annotations;
import org.onosproject.net.DefaultAnnotations;
import org.onosproject.net.DefaultDevice;
import org.onosproject.net.DefaultPort;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
......@@ -99,62 +100,16 @@ public final class PortCodec extends AnnotatedCodec<Port> {
/**
* Dummy Device which only holds DeviceId.
*/
private static final class DummyDevice implements Device {
private final DeviceId did;
private static final class DummyDevice extends DefaultDevice {
/**
* Constructs Dummy Device which only holds DeviceId.
*
* @param did device Id
*/
public DummyDevice(DeviceId did) {
this.did = did;
}
@Override
public Annotations annotations() {
return DefaultAnnotations.EMPTY;
}
@Override
public ProviderId providerId() {
return new ProviderId(did.uri().getScheme(), "PortCodec");
}
@Override
public DeviceId id() {
return did;
}
@Override
public Type type() {
return Type.SWITCH;
}
@Override
public String manufacturer() {
return "dummy";
}
@Override
public String hwVersion() {
return "0";
}
@Override
public String swVersion() {
return "0";
}
@Override
public String serialNumber() {
return "0";
}
@Override
public ChassisId chassisId() {
return new ChassisId();
super(new ProviderId(did.uri().getScheme(), "PortCodec"), did,
Type.SWITCH, "dummy", "0", "0", "0", new ChassisId(),
DefaultAnnotations.EMPTY);
}
}
}
......
......@@ -25,6 +25,7 @@ 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.ItemNotFoundException;
import org.onosproject.net.AbstractProjectableModel;
import org.onosproject.net.Device;
import org.onosproject.net.DeviceId;
import org.onosproject.net.device.DeviceService;
......@@ -72,15 +73,16 @@ public class DriverManager extends DefaultDriverProvider implements DriverAdminS
@Activate
protected void activate() {
AbstractProjectableModel.setDriverService(null, this);
log.info("Started");
}
@Deactivate
protected void deactivate() {
AbstractProjectableModel.setDriverService(this, null);
log.info("Stopped");
}
@Override
public Set<DriverProvider> getProviders() {
return ImmutableSet.copyOf(providers);
......