First implementation of BNG application

The application offers network level APIs for the BNG data plane.

An attachment (BngAttachment) at network level is identified by an arbitrary string.
The exposed APIs are key-value like APIs.

Change-Id: If0e484f487ea16dd8c7dd99642f75686e1dbc29a
diff --git a/app/src/main/java/org/opencord/bng/SimpleAttachmentEventHandler.java b/app/src/main/java/org/opencord/bng/SimpleAttachmentEventHandler.java
new file mode 100644
index 0000000..eb4ecd6
--- /dev/null
+++ b/app/src/main/java/org/opencord/bng/SimpleAttachmentEventHandler.java
@@ -0,0 +1,172 @@
+/*
+ * 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;
+
+import org.glassfish.jersey.internal.guava.Sets;
+import org.onlab.util.Tools;
+import org.onosproject.cfg.ComponentConfigService;
+import org.onosproject.core.ApplicationId;
+import org.onosproject.core.CoreService;
+import org.onosproject.store.service.StorageService;
+import org.osgi.service.component.ComponentContext;
+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.Modified;
+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.Dictionary;
+import java.util.Properties;
+import java.util.Set;
+
+import static org.opencord.bng.OsgiPropertyConstants.ENABLE_LOCAL_EVENT_HANDLER;
+import static org.opencord.bng.OsgiPropertyConstants.ENABLE_LOCAL_EVENT_HANDLER_DEFAULT;
+
+/**
+ * Service to intercept the PPPoE Handler events and trigger the creation of a
+ * new attachment in BNG service.
+ */
+@Component(immediate = true,
+        service = SimpleAttachmentEventHandler.class,
+        property = {
+                ENABLE_LOCAL_EVENT_HANDLER + ":Boolean=" + ENABLE_LOCAL_EVENT_HANDLER_DEFAULT,
+        }
+)
+public class SimpleAttachmentEventHandler {
+
+    private static final String ATTACHMENT_ID_GENERATOR_NAME = "SIMPLE_ATTACHMENT_EVENT_HANDLER_ATTACHMENT_ID";
+    private final Logger log = LoggerFactory.getLogger(getClass());
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected ComponentConfigService componentConfigService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected PppoeBngControlHandler pppoEHandlerRelay;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected BngService bngService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected CoreService coreService;
+
+    @Reference(cardinality = ReferenceCardinality.MANDATORY)
+    protected StorageService storageService;
+
+    /**
+     * Whether to enable of not the local attachment event handler, for debugging/development.
+     */
+    private boolean enableLocalEventHandler = ENABLE_LOCAL_EVENT_HANDLER_DEFAULT;
+    private InternalPppoeEvent pppoeEventListener = new InternalPppoeEvent();
+
+    // Map to store the attachment that this component has submitted through the BNG Service
+    private Set<String> addedAttachmentKeys;
+
+    private ApplicationId appId;
+
+    @Activate
+    protected void activate() {
+        appId = coreService.getAppId(BngManager.BNG_APP);
+        addedAttachmentKeys = Sets.newHashSet();
+        componentConfigService.registerProperties(getClass());
+        pppoEHandlerRelay.addListener(pppoeEventListener);
+        log.info("Simple Attachment Event Handler STARTED");
+    }
+
+    @Modified
+    public void modified(ComponentContext context) {
+        Dictionary<?, ?> properties = context != null ? context.getProperties() : new Properties();
+
+        Boolean localEvent = Tools.isPropertyEnabled(properties, ENABLE_LOCAL_EVENT_HANDLER);
+        if (localEvent != null) {
+            enableLocalEventHandler = localEvent;
+        }
+    }
+
+    @Deactivate
+    protected void deactivate() {
+        pppoEHandlerRelay.removeListener(pppoeEventListener);
+        addedAttachmentKeys = null;
+        componentConfigService.unregisterProperties(getClass(), false);
+        log.info("Simple Attachment Event Handler STOPPED");
+    }
+
+    /**
+     * Listener for BNG Attachment event for PPPoE attachments.
+     */
+    class InternalPppoeEvent implements PppoeEventListener {
+        @Override
+        public void event(PppoeEvent event) {
+            PppoeEventSubject eventInfo = event.subject();
+            String attachmentKey = BngUtils.calculateBngAttachmentKey(eventInfo);
+            switch (event.type()) {
+                case IPCP_CONF_ACK:
+                    log.debug("Received IPCP_CONF_ACK event, submit a new attachment");
+                    log.debug(eventInfo.toString());
+                    BngAttachment newAttachment = PppoeBngAttachment.builder()
+                            .withPppoeSessionId(eventInfo.getSessionId())
+                            .withApplicationId(appId)
+                            .withCTag(eventInfo.getcTag())
+                            .withSTag(eventInfo.getsTag())
+                            .withIpAddress(eventInfo.getIpAddress())
+                            .withMacAddress(eventInfo.getMacAddress())
+                            .withOnuSerial(eventInfo.getOnuSerialNumber())
+                            .withOltConnectPoint(eventInfo.getOltConnectPoint())
+                            .lineActivated(true)
+                            .build();
+                    if (!addedAttachmentKeys.add(attachmentKey)) {
+                        log.warn("Attachment ID already present. Re-submit the attachment");
+                    }
+                    bngService.setupAttachment(attachmentKey, newAttachment);
+                    break;
+
+                case SESSION_TERMINATION:
+                    attachmentKey =  BngUtils.calculateBngAttachmentKey(eventInfo);
+                    log.debug("Received SESSION_TERMINATION event, remove the attachment {}",
+                              attachmentKey);
+                    if (!addedAttachmentKeys.remove(attachmentKey)) {
+                        log.debug("Received SESSION_TERMINATION event, for attachment {} " +
+                                          "but attachment not present in local store", attachmentKey);
+                    } else {
+                        log.debug("Received SESSION_TERMINATION event, remove the attachment {}",
+                                  attachmentKey);
+                        bngService.removeAttachment(attachmentKey);
+                    }
+                    break;
+                case AUTH_FAILURE:
+                case AUTH_REQUEST:
+                case AUTH_SUCCESS:
+                case SESSION_INIT:
+                case IPCP_CONF_REQUEST:
+                case SESSION_CONFIRMATION:
+                    log.debug("Received event {}, nothing to do here.", event.type().toString());
+                    break;
+                default:
+                    throw new IllegalStateException("Unexpected value: " + event.type() +
+                                                            ", for attachment: " + attachmentKey);
+            }
+        }
+
+        @Override
+        public boolean isRelevant(PppoeEvent event) {
+            return enableLocalEventHandler &&
+                    event.subject().getClass().equals(PppoeEventSubject.class);
+        }
+    }
+}