Update to pom files and package organization of bng-app
Also, update maven repo to HTTPS.
Change-Id: I664f819b2a671a5e85dcfd3e87e333f1163366d5
diff --git a/app/src/main/java/org/opencord/bng/impl/BngManager.java b/app/src/main/java/org/opencord/bng/impl/BngManager.java
new file mode 100644
index 0000000..1f399e1
--- /dev/null
+++ b/app/src/main/java/org/opencord/bng/impl/BngManager.java
@@ -0,0 +1,539 @@
+/*
+ * Copyright 2019-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.opencord.bng.impl;
+
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.tuple.Pair;
+import org.onlab.packet.EthType;
+import org.onlab.packet.IpAddress;
+import org.onlab.packet.MacAddress;
+import org.onlab.packet.VlanId;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.net.ConnectPoint;
+import org.onosproject.net.DefaultAnnotations;
+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.behaviour.BngProgrammable;
+import org.onosproject.net.behaviour.BngProgrammable.BngProgrammableException;
+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.device.DeviceEvent;
+import org.onosproject.net.device.DeviceListener;
+import org.onosproject.net.device.DeviceService;
+import org.onosproject.net.host.DefaultHostDescription;
+import org.onosproject.net.host.HostDescription;
+import org.onosproject.net.host.HostProvider;
+import org.onosproject.net.host.HostProviderRegistry;
+import org.onosproject.net.host.HostProviderService;
+import org.onosproject.net.link.LinkService;
+import org.onosproject.net.provider.ProviderId;
+import org.opencord.bng.BngAttachment;
+import org.opencord.bng.BngService;
+import org.opencord.bng.PppoeBngAttachment;
+import org.opencord.bng.config.BngConfig;
+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.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.util.Map;
+import java.util.Optional;
+import java.util.Set;
+import java.util.concurrent.atomic.AtomicBoolean;
+import java.util.stream.Collectors;
+
+import static org.onosproject.net.config.basics.SubjectFactories.APP_SUBJECT_FACTORY;
+
+/**
+ * Implements the network level BNG service API to manage attachments.
+ */
+@Component(immediate = true)
+public class BngManager implements HostProvider, BngService {
+ public static final String BNG_APP = "org.opencord.bng";
+
+ private static final ProviderId PROVIDER_ID = new ProviderId("bngapp", BngManager.BNG_APP);
+
+ private final Logger log = LoggerFactory.getLogger(getClass());
+ private final AtomicBoolean bnguInitialized = new AtomicBoolean(false);
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected LinkService linkService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected DeviceService deviceService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected CoreService coreService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected NetworkConfigRegistry cfgService;
+
+ @Reference(cardinality = ReferenceCardinality.MANDATORY)
+ protected HostProviderRegistry providerRegistry;
+
+ private ConfigFactory<ApplicationId, BngConfig> cfgFactory = new ConfigFactory<>(
+ APP_SUBJECT_FACTORY,
+ BngConfig.class,
+ BngConfig.KEY) {
+ @Override
+ public BngConfig createConfig() {
+ return new BngConfig();
+ }
+ };
+ private BngProgrammable bngProgrammable;
+ private DeviceId bngDeviceId;
+ private InternalDeviceListener deviceListener;
+ private InternalConfigListener cfgListener;
+ private HostProviderService hostProviderService;
+ // TODO: add support for other attachment type
+ private Map<String, Pair<BngAttachment, HostId>> registeredAttachment;
+ private ApplicationId appId;
+
+ @Activate
+ protected void activate() {
+ appId = coreService.registerApplication(BNG_APP);
+ hostProviderService = providerRegistry.register(this);
+ registeredAttachment = Maps.newHashMap();
+ bngProgrammable = null;
+ bngDeviceId = null;
+ deviceListener = new InternalDeviceListener();
+ cfgListener = new InternalConfigListener();
+ cfgService.addListener(cfgListener);
+ cfgService.registerConfigFactory(cfgFactory);
+
+ // Update the BNG relay configuration
+ updateConfig();
+
+ deviceService.addListener(deviceListener);
+
+ log.info("BNG app activated");
+ }
+
+ @Deactivate
+ protected void deactivate() {
+ providerRegistry.unregister(this);
+ if (bngProgrammableAvailable()) {
+ try {
+ bngProgrammable.cleanUp(appId);
+ } catch (BngProgrammableException e) {
+ log.error("Error cleaning-up the BNG pipeline, {}", e.getMessage());
+ }
+ }
+ deviceService.removeListener(deviceListener);
+ cfgService.removeListener(cfgListener);
+ cfgService.unregisterConfigFactory(cfgFactory);
+ registeredAttachment = null;
+ bnguInitialized.set(false);
+ log.info("BNG app deactivated");
+ }
+
+ @Override
+ public void setupAttachment(String attachmentKey, BngAttachment attachment) {
+ // FIXME: the update case is not completely clear. Should the programAttachment method clean up the counters?
+ assert attachment.type().equals(BngProgrammable.Attachment.AttachmentType.PPPoE);
+ boolean updating = false;
+ var alreadyRegAttachment = registeredAttachment.get(attachmentKey);
+ if (alreadyRegAttachment == null) {
+ log.info("Registering a new attachment: {}", attachment.toString());
+ } else if (!attachment.equals(alreadyRegAttachment.getLeft())) {
+ log.info("Updating the attachment: {}", attachment.toString());
+ updating = true;
+ } else {
+ log.info("Attachment already registered: {}", attachment.toString());
+ return;
+ }
+ // FIXME: it could register anyway the attachment but do not program it on the BNG data-plane device.
+ if (attachment.type() != BngProgrammable.Attachment.AttachmentType.PPPoE) {
+ log.warn("Attachment type not supported, rejecting attachment: {}", attachmentKey);
+ return;
+ }
+ var pppoeAttachment = (PppoeBngAttachment) attachment;
+ // Retrieve the connect point on the ASG device
+ var asgConnectPoint = getAsgConnectPoint(pppoeAttachment.oltConnectPoint()).orElseThrow();
+ final HostId hostId = HostId.hostId(attachment.macAddress(), attachment.sTag());
+ final HostDescription hostDescription = createHostDescription(
+ attachment.cTag(), attachment.sTag(),
+ attachment.macAddress(), attachment.ipAddress(),
+ asgConnectPoint, pppoeAttachment.oltConnectPoint(), pppoeAttachment.onuSerial());
+
+ // Make sure that bngProgrammable is available and if so that the attachment is connected to the bngProgrammable
+ if (bngProgrammableAvailable() && isCorrectlyConnected(asgConnectPoint)) {
+ try {
+ programAttachment(attachment, hostId, hostDescription, updating);
+ } catch (BngProgrammableException ex) {
+ log.error("Attachment not created: " + ex.getMessage());
+ }
+ } else {
+ // If the BNG user plane is not available, or the attachment is not connected to
+ // the correct BNG user planee, accept anyway the attachment.
+ // Check if the attachment is correctly connected to the BNG device when that device will show up.
+ log.info("BNG user plane not available, attachment accepted but not programmed");
+ }
+ log.info("PPPoE Attachment created/updated: {}", pppoeAttachment);
+ registeredAttachment.put(attachmentKey, Pair.of(pppoeAttachment, hostId));
+ }
+
+ private Optional<ConnectPoint> getAsgConnectPoint(ConnectPoint oltConnectPoint) {
+ try {
+ // Here I suppose that each OLT can be connected to a SINGLE ASG that is BNG U capable
+ return Optional.of(linkService.getDeviceEgressLinks(oltConnectPoint.deviceId()).stream()
+ .filter(link -> isBngProgrammable(link.dst().deviceId()))
+ .map(link -> link.dst())
+ .collect(Collectors.toList())
+ .get(0));
+
+ } catch (IndexOutOfBoundsException ex) {
+ return Optional.empty();
+ }
+ }
+
+ /**
+ * Setup of an attachment. Before calling this method, make sure that BNG
+ * programmable is available.
+ *
+ * @param attachment
+ * @param hostId
+ * @param hostDescription
+ * @param update
+ * @throws BngProgrammableException
+ */
+ private void programAttachment(BngAttachment attachment, HostId hostId,
+ HostDescription hostDescription, boolean update)
+ throws BngProgrammableException {
+ assert bngProgrammableAvailable();
+ bngProgrammable.setupAttachment(attachment);
+ if (!update) {
+ bngProgrammable.resetCounters(attachment);
+ }
+ // Trigger host creation in ONOS
+ hostProviderService.hostDetected(hostId, hostDescription, true);
+ }
+
+ /**
+ * Create an host description from the attachment information.
+ *
+ * @param cTag Vlan C-TAG.
+ * @param sTag Vlan S-TAG.
+ * @param hostMac MAC address of the attachment.
+ * @param hostIp IP address of the attachment.
+ * @param asgConnectPoint Attachment connect point from the ASG switch
+ * perspective.
+ * @param oltConnectPoint Attachment connect point from the OLT
+ * perspective.
+ * @param onuSerialNumber ONU Serial Number.
+ * @return Host description of the attachment
+ */
+ private HostDescription createHostDescription(VlanId cTag, VlanId sTag,
+ MacAddress hostMac,
+ IpAddress hostIp,
+ ConnectPoint asgConnectPoint,
+ ConnectPoint oltConnectPoint,
+ String onuSerialNumber) {
+// Set<HostLocation> hostLocation = Set.of(new HostLocation(oltConnectPoint.deviceId(),
+// oltConnectPoint.port(),
+// System.currentTimeMillis()));
+// var auxLocation = String.join(",", asgConnectPoint.toString());
+ // FIXME: remove this hostLocation and decomment the above rows when aux-location patch will be merged
+ Set<HostLocation> hostLocation = Set.of(new HostLocation(asgConnectPoint.deviceId(),
+ asgConnectPoint.port(),
+ System.currentTimeMillis()));
+ var annotations = DefaultAnnotations.builder()
+// .set(AUX_LOCATIONS_ANNOTATION, auxLocation)
+ .set(ONU_ANNOTATION, onuSerialNumber)
+ .build();
+ Set<IpAddress> ips = hostIp != null
+ ? ImmutableSet.of(hostIp) : ImmutableSet.of();
+ return new DefaultHostDescription(hostMac, sTag,
+ hostLocation,
+ ips, cTag, EthType.EtherType.QINQ.ethType(),
+ false, annotations);
+ }
+
+ @Override
+ public void removeAttachment(String attachmentKey) {
+ assert attachmentKey != null;
+ if (!registeredAttachment.containsKey(attachmentKey)) {
+ log.info("Attachment cannot be removed if it wasn't registered");
+ return;
+ }
+ var regAttachment = registeredAttachment.get(attachmentKey).getLeft();
+
+ final HostId hostToBeRemoved = HostId.hostId(regAttachment.macAddress(), regAttachment.sTag());
+ registeredAttachment.remove(attachmentKey);
+ // Try to remove host even if the BNG user plane device is not available
+ hostProviderService.hostVanished(hostToBeRemoved);
+ if (bngProgrammableAvailable()) {
+ bngProgrammable.removeAttachment(regAttachment);
+ } else {
+ log.info("BNG user plane not available!");
+ }
+ log.info("Attachment {} removed successfully!", regAttachment);
+ }
+
+
+ @Override
+ public Map<String, BngAttachment> getAttachments() {
+ return Maps.asMap(registeredAttachment.keySet(),
+ key -> registeredAttachment.get(key).getLeft());
+ }
+
+ @Override
+ public BngAttachment getAttachment(String attachmentKey) {
+ return registeredAttachment.getOrDefault(attachmentKey, Pair.of(null, null))
+ .getLeft();
+ }
+
+ /**
+ * Check if the given connect point is part of the BNG user plane device.
+ * Before calling this method, make sure that bngProgrammable is available.
+ *
+ * @param asgConnectPoint The connect point to check
+ * @return
+ */
+ private boolean isCorrectlyConnected(ConnectPoint asgConnectPoint) {
+ assert bngProgrammableAvailable();
+ return asgConnectPoint.deviceId().equals(bngProgrammable.data().deviceId());
+ }
+
+ /**
+ * Setup of the BNG user plane device. This method will cleanup the BNG
+ * pipeline, initialize it and then submit all the attachment already
+ * registered.
+ *
+ * @param deviceId BNG user plane device ID
+ */
+ private void setBngDevice(DeviceId deviceId) {
+ synchronized (bnguInitialized) {
+ if (bnguInitialized.get()) {
+ log.debug("BNG device {} already initialized", deviceId);
+ return;
+ }
+ if (!isBngProgrammable(deviceId)) {
+ log.warn("{} is not BNG-U", deviceId);
+ return;
+ }
+ if (bngProgrammable != null && !bngProgrammable.data().deviceId().equals(deviceId)) {
+ log.error("Change of the BNG-U while BNG-U device is available is not supported!");
+ return;
+ }
+
+ bngProgrammable = deviceService.getDevice(deviceId).as(BngProgrammable.class);
+ log.info("Program BNG-U device {}", deviceId);
+
+ // Initialize behavior
+ try {
+ bngProgrammable.cleanUp(appId);
+ bngProgrammable.init(appId);
+ // FIXME: we can improve this re-registration, keeping track of which attachment
+ // already has the flow rules submitted in the flow rule subsystem.
+ // In this way we do not need to cleanUp the bngProgrammable every time it come back online.
+ // If there is any already registered attachment, try to re-setup their attachment.
+ resubmitRegisteredAttachment();
+
+ bnguInitialized.set(true);
+ } catch (BngProgrammableException e) {
+ log.error("Error in BNG user plane, {}", e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Resubmit all the attachments to the BNG user plane device. Before calling
+ * this method, make sure that bngProgrammable is available
+ *
+ * @throws BngProgrammableException when error in BNG user plane device.
+ */
+ private void resubmitRegisteredAttachment() throws BngProgrammableException {
+ assert bngProgrammableAvailable();
+ for (var registeredAttachemnt : registeredAttachment.entrySet()) {
+ var attachment = registeredAttachemnt.getValue().getLeft();
+ var host = registeredAttachemnt.getValue().getRight();
+ var attachentKey = registeredAttachemnt.getKey();
+ var asgConnectPoint = getAsgConnectPoint(attachment.oltConnectPoint());
+ if (attachment.type() != BngProgrammable.Attachment.AttachmentType.PPPoE) {
+ log.info("Unsupported attachment: {}", attachentKey);
+ continue;
+ }
+ if (asgConnectPoint.isPresent() && isCorrectlyConnected(asgConnectPoint.orElseThrow())) {
+ HostDescription hostDescription = createHostDescription(
+ attachment.cTag(), attachment.sTag(),
+ attachment.macAddress(), attachment.ipAddress(),
+ asgConnectPoint.orElseThrow(), attachment.oltConnectPoint(),
+ attachment.onuSerial());
+ // When resubmitting registered attachment act as the attachment is being setting up.
+ programAttachment(attachment, host, hostDescription, false);
+ } else {
+ log.info("Attachment is not connected to a valid BNG user plane: {}", attachment);
+ }
+ }
+ }
+
+ /**
+ * Unset the BNG user plane device. If available it will be cleaned-up.
+ */
+ private void unsetBngDevice() {
+ synchronized (bnguInitialized) {
+ if (bngProgrammable != null) {
+ try {
+ bngProgrammable.cleanUp(appId);
+ } catch (BngProgrammableException e) {
+ log.error("Error in BNG user plane, {}", e.getMessage());
+ }
+ bngProgrammable = null;
+ bnguInitialized.set(false);
+ }
+ }
+ }
+
+ /**
+ * Check if the device is registered and is BNG user plane.
+ *
+ * @param deviceId
+ * @return
+ */
+ private boolean isBngProgrammable(DeviceId deviceId) {
+ final Device device = deviceService.getDevice(deviceId);
+ return device != null && device.is(BngProgrammable.class);
+ }
+
+ /**
+ * Check if the BNG user plane is available.
+ *
+ * @return
+ * @throws BngProgrammableException
+ */
+ private boolean bngProgrammableAvailable() {
+ return bngProgrammable != null;
+ }
+
+ private void bngUpdateConfig(BngConfig config) {
+ if (config.isValid()) {
+ bngDeviceId = config.getBnguDeviceId();
+ setBngDevice(bngDeviceId);
+ }
+ }
+
+ @Override
+ public DeviceId getBngDeviceId() {
+ return bngDeviceId;
+ }
+
+ /**
+ * Updates BNG app configuration.
+ */
+ private void updateConfig() {
+ BngConfig bngConfig = cfgService.getConfig(appId, BngConfig.class);
+ if (bngConfig != null) {
+ bngUpdateConfig(bngConfig);
+ }
+ }
+
+ @Override
+ public void triggerProbe(Host host) {
+ // Do nothing here
+ }
+
+ @Override
+ public ProviderId id() {
+ return PROVIDER_ID;
+ }
+
+ /**
+ * React to new devices. The first device recognized to have BNG-U
+ * functionality is taken as BNG-U device.
+ */
+ private class InternalDeviceListener implements DeviceListener {
+ @Override
+ public void event(DeviceEvent event) {
+ DeviceId deviceId = event.subject().id();
+ if (deviceId.equals(bngDeviceId)) {
+ switch (event.type()) {
+ case DEVICE_ADDED:
+ case DEVICE_UPDATED:
+ case DEVICE_AVAILABILITY_CHANGED:
+ // FIXME: do I need the IF?
+ //if (deviceService.isAvailable(deviceId)) {
+ log.warn("Event: {}, SETTING BNG device", event.type());
+ setBngDevice(deviceId);
+ //}
+ break;
+ case DEVICE_REMOVED:
+ case DEVICE_SUSPENDED:
+ unsetBngDevice();
+ break;
+ case PORT_ADDED:
+ case PORT_UPDATED:
+ case PORT_REMOVED:
+ case PORT_STATS_UPDATED:
+ break;
+ default:
+ log.warn("Unknown device event type {}", event.type());
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Listener for network config events.
+ */
+ private class InternalConfigListener implements NetworkConfigListener {
+ @Override
+ public void event(NetworkConfigEvent event) {
+ switch (event.type()) {
+ case CONFIG_UPDATED:
+ case CONFIG_ADDED:
+ event.config().ifPresent(config -> {
+ bngUpdateConfig((BngConfig) config);
+ log.info("{} updated", config.getClass().getSimpleName());
+ });
+ break;
+ case CONFIG_REMOVED:
+ event.prevConfig().ifPresent(config -> {
+ unsetBngDevice();
+ log.info("{} removed", config.getClass().getSimpleName());
+ });
+ break;
+ case CONFIG_REGISTERED:
+ case CONFIG_UNREGISTERED:
+ break;
+ default:
+ log.warn("Unsupported event type {}", event.type());
+ break;
+ }
+ }
+
+ @Override
+ public boolean isRelevant(NetworkConfigEvent event) {
+ if (event.configClass().equals(BngConfig.class)) {
+ return true;
+ }
+ log.debug("Ignore irrelevant event class {}", event.configClass().getName());
+ return false;
+ }
+ }
+}