Hyunsun Moon

Refactored to handle service instance by service type

- Added service instance handler
- Implemented dummy, vsg, and olt agent instance handler

Change-Id: Id3edd5eecb1caadf0f835cb10a952100e18b283b
......@@ -11,6 +11,7 @@ COMPILE_DEPS = [
'//core/store/serializers:onos-core-serializers',
'//apps/dhcp/api:onos-apps-dhcp-api',
'//apps/xosclient:onos-apps-xosclient',
'//apps/cordconfig:onos-apps-cordconfig',
'//protocols/ovsdb/api:onos-protocols-ovsdb-api',
'//protocols/ovsdb/rfc:onos-protocols-ovsdb-rfc',
]
......@@ -35,5 +36,5 @@ onos_app (
included_bundles = BUNDLES,
excluded_bundles = EXCLUDED_BUNDLES,
description = 'APIs for interacting with the CORD VTN application.',
required_apps = [ 'org.onosproject.xosclient', 'org.onosproject.dhcp', 'org.onosproject.ovsdb' ],
required_apps = [ 'org.onosproject.cord-config', 'org.onosproject.xosclient', 'org.onosproject.dhcp', 'org.onosproject.ovsdb' ],
)
......
......@@ -18,7 +18,7 @@
category="Traffic Steering" url="http://onosproject.org" title="CORD Virtual Tenant Network"
featuresRepo="mvn:${project.groupId}/${project.artifactId}/${project.version}/xml/features"
features="${project.artifactId}"
apps="org.onosproject.ovsdb-base,org.onosproject.dhcp,org.onosproject.xosclient">
apps="org.onosproject.ovsdb-base,org.onosproject.dhcp,org.onosproject.xosclient,org.onosproject.cord-config">
<description>${project.description}</description>
<artifact>mvn:${project.groupId}/onos-app-cordvtn/${project.version}</artifact>
</app>
......
......@@ -108,6 +108,11 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>org.onosproject</groupId>
<artifactId>onos-cord-config</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>com.jcraft</groupId>
<artifactId>jsch</artifactId>
<version>0.1.53</version>
......
......@@ -20,6 +20,7 @@ import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.IpPrefix;
import org.onlab.packet.MacAddress;
import org.onlab.packet.TpPort;
import org.onosproject.core.ApplicationId;
......@@ -46,6 +47,7 @@ public class CordVtnConfig extends Config<ApplicationId> {
public static final String GATEWAY_IP = "gatewayIp";
public static final String GATEWAY_MAC = "gatewayMac";
public static final String LOCAL_MANAGEMENT_IP = "localManagementIp";
public static final String MANAGEMENT_IP = "managementIpRange";
public static final String OVSDB_PORT = "ovsdbPort";
public static final String CORDVTN_NODES = "nodes";
......@@ -187,6 +189,25 @@ public class CordVtnConfig extends Config<ApplicationId> {
}
/**
* Returns management IP address range.
*
* @return management network ip prefix, or null
*/
public IpPrefix managementIpRange() {
JsonNode jsonNode = object.get(MANAGEMENT_IP);
if (jsonNode == null) {
return null;
}
try {
return IpPrefix.valueOf(jsonNode.asText());
} catch (IllegalArgumentException e) {
log.error("{}:{} wrong address format", MANAGEMENT_IP, jsonNode);
return null;
}
}
/**
* Returns XOS access information.
*
* @return XOS access, or null
......
......@@ -15,14 +15,8 @@
*/
package org.onosproject.cordvtn.api;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.net.ConnectPoint;
import org.onosproject.net.HostId;
import org.onosproject.xosclient.api.VtnServiceId;
import java.util.Map;
/**
* Service for provisioning overlay virtual networks on compute nodes.
*/
......@@ -31,21 +25,6 @@ public interface CordVtnService {
String CORDVTN_APP_ID = "org.onosproject.cordvtn";
/**
* Adds a new VM on a given node and connect point.
*
* @param node cordvtn node
* @param connectPoint connect point
*/
void addServiceVm(CordVtnNode node, ConnectPoint connectPoint);
/**
* Removes a VM from a given node and connect point.
*
* @param connectPoint connect point
*/
void removeServiceVm(ConnectPoint connectPoint);
/**
* Creates dependencies for a given tenant service.
*
* @param tServiceId id of the service which has a dependency
......@@ -62,14 +41,4 @@ public interface CordVtnService {
* @param pServiceId id of the service which provide dependency
*/
void removeServiceDependency(VtnServiceId tServiceId, VtnServiceId pServiceId);
/**
* Updates virtual service gateways.
*
* @param vSgHost host id of vSG host
* @param serviceVlan service vlan id
* @param vSgs map of ip and mac address of vSGs running in this vSG host
*/
void updateVirtualSubscriberGateways(HostId vSgHost, String serviceVlan,
Map<IpAddress, MacAddress> vSgs);
}
......
/*
* 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.cordvtn.api;
import com.google.common.base.Strings;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.MacAddress;
import org.onosproject.net.DeviceId;
import org.onosproject.net.Host;
import org.onosproject.net.PortNumber;
import org.onosproject.xosclient.api.VtnPortId;
import org.onosproject.xosclient.api.VtnService;
import org.onosproject.xosclient.api.VtnServiceId;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Provides methods to help to handle network service instance.
*/
public final class Instance {
public static final String SERVICE_ID = "serviceId";
public static final String SERVICE_TYPE = "serviceType";
public static final String PORT_ID = "vtnPortId";
public static final String CREATE_TIME = "createTime";
public static final String NESTED_INSTANCE = "nestedInstance";
public static final String TRUE = "true";
private final Host host;
/**
* Default constructor.
*
* @param instance host object of this instance
*/
private Instance(Host instance) {
this.host = instance;
}
/**
* Returns host object of this instance.
*
* @return host
*/
public Host host() {
return this.host;
}
/**
* Returns new instance.
*
* @param host host object of this instance
* @return instance
*/
public static Instance of(Host host) {
checkNotNull(host);
checkArgument(!Strings.isNullOrEmpty(host.annotations().value(SERVICE_ID)));
checkArgument(!Strings.isNullOrEmpty(host.annotations().value(SERVICE_TYPE)));
checkArgument(!Strings.isNullOrEmpty(host.annotations().value(PORT_ID)));
checkArgument(!Strings.isNullOrEmpty(host.annotations().value(CREATE_TIME)));
return new Instance(host);
}
/**
* Returns service ID of a given host.
*
* @return vtn service id
*/
public VtnServiceId serviceId() {
String serviceId = host.annotations().value(SERVICE_ID);
return VtnServiceId.of(serviceId);
}
/**
* Returns service type of a given host.
*
* @return vtn service type
*/
public VtnService.ServiceType serviceType() {
String serviceType = host.annotations().value(SERVICE_TYPE);
return VtnService.ServiceType.valueOf(serviceType);
}
/**
* Returns port ID of a given host.
*
* @return vtn port id
*/
public VtnPortId portId() {
String portId = host.annotations().value(PORT_ID);
return VtnPortId.of(portId);
}
/**
* Returns if the instance is nested container or not.
*
* @return true if it's nested container; false otherwise
*/
public boolean isNestedInstance() {
return host.annotations().value(NESTED_INSTANCE) != null;
}
/**
* Returns MAC address of this instance.
*
* @return mac address
*/
public MacAddress mac() {
return host.mac();
}
/**
* Returns IP address of this instance.
*
* @return ip address
*/
public Ip4Address ipAddress() {
// assume all instance has only one IP address, and only IP4 is supported now
return host.ipAddresses().stream().findFirst().get().getIp4Address();
}
/**
* Returns device ID of this host.
*
* @return device id
*/
public DeviceId deviceId() {
return host.location().deviceId();
}
/**
* Returns the port number where this host is.
*
* @return port number
*/
public PortNumber portNumber() {
return host.location().port();
}
/**
* Returns annotation value with a given key.
*
* @param annotationKey annotation key
* @return annotation value
*/
public String getAnnotation(String annotationKey) {
return host.annotations().value(annotationKey);
}
@Override
public String toString() {
return host.toString();
}
}
/*
* 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.cordvtn.api;
/**
* Handles service instance detection and removal.
*/
public interface InstanceHandler {
/**
* Handles newly detected instance.
*
* @param instance instance
*/
void instanceDetected(Instance instance);
/**
* Handles removed instance.
*
* @param instance instance
*/
void instanceRemoved(Instance instance);
}
......@@ -18,7 +18,7 @@ package org.onosproject.cordvtn.cli;
import org.apache.karaf.shell.commands.Command;
import org.onosproject.cli.AbstractShellCommand;
import org.onosproject.cordvtn.impl.CordVtnNodeManager;
import org.onosproject.cordvtn.impl.CordVtnPipeline;
/**
* Deletes nodes from the service.
......@@ -29,8 +29,8 @@ public class CordVtnFlushRules extends AbstractShellCommand {
@Override
protected void execute() {
CordVtnNodeManager nodeManager = AbstractShellCommand.get(CordVtnNodeManager.class);
nodeManager.flushRules();
CordVtnPipeline pipeline = AbstractShellCommand.get(CordVtnPipeline.class);
pipeline.flushRules();
print("Successfully flushed");
}
}
......
......@@ -22,6 +22,7 @@ import org.onlab.packet.Ethernet;
import org.onlab.packet.Ip4Address;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.cordvtn.api.Instance;
import org.onosproject.core.ApplicationId;
import org.onosproject.net.Host;
import org.onosproject.net.flow.DefaultTrafficSelector;
......@@ -166,9 +167,9 @@ public class CordVtnArpProxy {
* Emits gratuitous ARP when a gateway mac address has been changed.
*
* @param gatewayIp gateway ip address to update MAC
* @param hosts set of hosts to send gratuitous ARP packet
* @param instances set of instances to send gratuitous ARP packet
*/
public void sendGratuitousArpForGateway(IpAddress gatewayIp, Set<Host> hosts) {
public void sendGratuitousArpForGateway(IpAddress gatewayIp, Set<Instance> instances) {
MacAddress gatewayMac = gateways.get(gatewayIp.getIp4Address());
if (gatewayMac == null) {
log.debug("Gateway {} is not registered to ARP proxy", gatewayIp.toString());
......@@ -176,13 +177,13 @@ public class CordVtnArpProxy {
}
Ethernet ethArp = buildGratuitousArp(gatewayIp.getIp4Address(), gatewayMac);
hosts.stream().forEach(host -> {
instances.stream().forEach(instance -> {
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(host.location().port())
.setOutput(instance.portNumber())
.build();
packetService.emit(new DefaultOutboundPacket(
host.location().deviceId(),
instance.deviceId(),
treatment,
ByteBuffer.wrap(ethArp.serialize())));
});
......
/*
* 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.cordvtn.impl.service;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.onosproject.cordvtn.api.Instance;
import org.onosproject.cordvtn.api.InstanceHandler;
import org.onosproject.cordvtn.impl.CordVtnInstanceHandler;
import org.onosproject.xosclient.api.VtnService;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;
/**
* Provides network connectivity for dummy service instances.
*/
@Component(immediate = true)
public class DummyInstanceHandler extends CordVtnInstanceHandler implements InstanceHandler {
@Activate
protected void activate() {
serviceType = VtnService.ServiceType.DUMMY;
eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-dummy", "event-handler"));
super.activate();
}
@Deactivate
protected void deactivate() {
super.deactivate();
}
@Override
public void instanceDetected(Instance instance) {
super.instanceDetected(instance);
}
@Override
public void instanceRemoved(Instance instance) {
super.instanceRemoved(instance);
}
}
/*
* 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.cordvtn.impl.service;
import com.google.common.collect.Maps;
import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Deactivate;
import org.onlab.packet.Ethernet;
import org.onlab.packet.IpPrefix;
import org.onosproject.cordconfig.access.AccessAgentConfig;
import org.onosproject.cordconfig.access.AccessAgentData;
import org.onosproject.cordvtn.api.CordVtnConfig;
import org.onosproject.cordvtn.api.Instance;
import org.onosproject.cordvtn.api.InstanceHandler;
import org.onosproject.cordvtn.impl.CordVtnInstanceHandler;
import org.onosproject.net.DeviceId;
import org.onosproject.net.PortNumber;
import org.onosproject.net.config.ConfigFactory;
import org.onosproject.net.config.NetworkConfigEvent;
import org.onosproject.net.config.NetworkConfigListener;
import org.onosproject.net.config.basics.SubjectFactories;
import org.onosproject.net.flow.DefaultFlowRule;
import org.onosproject.net.flow.DefaultTrafficSelector;
import org.onosproject.net.flow.DefaultTrafficTreatment;
import org.onosproject.net.flow.FlowRule;
import org.onosproject.net.flow.TrafficSelector;
import org.onosproject.net.flow.TrafficTreatment;
import org.onosproject.xosclient.api.VtnService;
import java.util.Map;
import java.util.Set;
import static java.util.concurrent.Executors.newSingleThreadScheduledExecutor;
import static org.onlab.util.Tools.groupedThreads;
import static org.onosproject.cordvtn.impl.CordVtnPipeline.PRIORITY_MANAGEMENT;
import static org.onosproject.cordvtn.impl.CordVtnPipeline.TABLE_ACCESS_TYPE;
/**
* Provides network connectivity for OLT agent instances.
*/
@Component(immediate = true)
public class OltAgentInstanceHandler extends CordVtnInstanceHandler implements InstanceHandler {
private static final Class<AccessAgentConfig> CONFIG_CLASS = AccessAgentConfig.class;
private ConfigFactory<DeviceId, AccessAgentConfig> configFactory =
new ConfigFactory<DeviceId, AccessAgentConfig>(
SubjectFactories.DEVICE_SUBJECT_FACTORY, CONFIG_CLASS, "accessAgent") {
@Override
public AccessAgentConfig createConfig() {
return new AccessAgentConfig();
}
};
private Map<DeviceId, AccessAgentData> oltAgentData = Maps.newConcurrentMap();
private IpPrefix mgmtIpRange = null;
@Activate
protected void activate() {
eventExecutor = newSingleThreadScheduledExecutor(groupedThreads("onos/cordvtn-olt", "event-handler"));
serviceType = VtnService.ServiceType.OLT_AGENT;
configRegistry.registerConfigFactory(configFactory);
configListener = new InternalConfigListener();
super.activate();
}
@Deactivate
protected void deactivate() {
super.deactivate();
}
@Override
public void instanceDetected(Instance instance) {
log.info("OLT agent instance detected {}", instance);
managementAccessRule(instance.deviceId(), true);
// TODO implement
}
@Override
public void instanceRemoved(Instance instance) {
log.info("OLT agent instance removed {}", instance);
if (getInstances(instance.serviceId()).isEmpty()) {
nodeManager.completeNodes().stream().forEach(node ->
managementAccessRule(node.intBrId(), false));
}
// TODO implement
}
private void managementAccessRule(DeviceId deviceId, boolean install) {
// TODO remove this rule after long term management network is done
if (mgmtIpRange != null) {
TrafficSelector selector = DefaultTrafficSelector.builder()
.matchEthType(Ethernet.TYPE_IPV4)
.matchIPDst(mgmtIpRange)
.build();
TrafficTreatment treatment = DefaultTrafficTreatment.builder()
.setOutput(PortNumber.LOCAL)
.build();
FlowRule flowRule = DefaultFlowRule.builder()
.fromApp(appId)
.withSelector(selector)
.withTreatment(treatment)
.withPriority(PRIORITY_MANAGEMENT)
.forDevice(deviceId)
.forTable(TABLE_ACCESS_TYPE)
.makePermanent()
.build();
pipeline.processFlowRule(install, flowRule);
}
}
private void readAccessAgentConfig() {
Set<DeviceId> deviceSubjects = configRegistry.getSubjects(DeviceId.class, CONFIG_CLASS);
deviceSubjects.stream().forEach(subject -> {
AccessAgentConfig config = configRegistry.getConfig(subject, CONFIG_CLASS);
if (config != null) {
oltAgentData.put(subject, config.getAgent());
}
});
}
@Override
protected void readConfiguration() {
CordVtnConfig config = configRegistry.getConfig(appId, CordVtnConfig.class);
if (config == null) {
log.debug("No configuration found");
return;
}
osAccess = config.openstackAccess();
xosAccess = config.xosAccess();
mgmtIpRange = config.managementIpRange();
}
public class InternalConfigListener implements NetworkConfigListener {
@Override
public void event(NetworkConfigEvent event) {
switch (event.type()) {
case CONFIG_UPDATED:
case CONFIG_ADDED:
if (event.configClass().equals(CordVtnConfig.class)) {
readConfiguration();
} else if (event.configClass().equals(CONFIG_CLASS)) {
readAccessAgentConfig();
}
break;
default:
break;
}
}
}
}
/*
* 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.
*/
/**
* Implementation of instance handlers for various network services.
*/
package org.onosproject.cordvtn.impl.service;
\ No newline at end of file
......@@ -18,9 +18,10 @@ package org.onosproject.cordvtn.rest;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Maps;
import org.onlab.osgi.DefaultServiceDirectory;
import org.onlab.packet.IpAddress;
import org.onlab.packet.MacAddress;
import org.onosproject.cordvtn.api.CordVtnService;
import org.onosproject.cordvtn.impl.service.VsgInstanceHandler;
import org.onosproject.net.HostId;
import org.onosproject.rest.AbstractWebResource;
import org.slf4j.Logger;
......@@ -57,7 +58,7 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource {
private static final String STAG_PREFIX = "stag-";
private static final int STAG_BEGIN_INDEX = 5;
private final CordVtnService service = get(CordVtnService.class);
private final VsgInstanceHandler service = DefaultServiceDirectory.getService(VsgInstanceHandler.class);
@POST
@Consumes(MediaType.APPLICATION_JSON)
......@@ -74,6 +75,7 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource {
public Response updatePorts(@PathParam("id") String id, InputStream input) {
log.debug(String.format(PORTS_MESSAGE, "update"));
// TODO get vSG updates from XOS to CORD VTN service directly
try {
ObjectMapper mapper = new ObjectMapper();
JsonNode jsonNode = mapper.readTree(input).get(PORT);
......@@ -88,17 +90,16 @@ public class NeutronMl2PortsWebResource extends AbstractWebResource {
// this is allowed address pairs updates
MacAddress mac = MacAddress.valueOf(jsonNode.path(MAC_ADDRESS).asText());
Map<IpAddress, MacAddress> vSgs = Maps.newHashMap();
Map<IpAddress, MacAddress> vsgInstances = Maps.newHashMap();
jsonNode.path(ADDRESS_PAIRS).forEach(addrPair -> {
IpAddress pairIp = IpAddress.valueOf(addrPair.path(IP_ADDERSS).asText());
MacAddress pairMac = MacAddress.valueOf(addrPair.path(MAC_ADDRESS).asText());
vSgs.put(pairIp, pairMac);
vsgInstances.put(pairIp, pairMac);
});
service.updateVirtualSubscriberGateways(
HostId.hostId(mac),
name.substring(STAG_BEGIN_INDEX),
vSgs);
service.updateVsgInstances(HostId.hostId(mac),
name.substring(STAG_BEGIN_INDEX),
vsgInstances);
} catch (Exception e) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
}
......