vOL-1136: ADTRAN OLT: Support new shared KPI and Alarm libraries

Change-Id: Ief963b73f232e79862f39912184588abe88de24f
diff --git a/voltha/adapters/adtran_olt/adtran_device_handler.py b/voltha/adapters/adtran_olt/adtran_device_handler.py
index 438cb62..5fd5547 100644
--- a/voltha/adapters/adtran_olt/adtran_device_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_device_handler.py
@@ -32,8 +32,8 @@
 from voltha.protos.logical_device_pb2 import LogicalDevice
 from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_switch_features, OFPC_PORT_STATS, \
     OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
-from alarms.adapter_alarms import AdapterAlarms
-from pki.olt_pm_metrics import OltPmMetrics
+from voltha.extensions.alarms.adapter_alarms import AdapterAlarms
+from voltha.extensions.pki.olt.olt_pm_metrics import OltPmMetrics
 from common.utils.asleep import asleep
 
 _ = third_party
@@ -105,7 +105,7 @@
         self.multicast_vlans = [DEFAULT_MULTICAST_VLAN]
         self.untagged_vlan = DEFAULT_UNTAGGED_VLAN
         self.utility_vlan = DEFAULT_UTILITY_VLAN
-        self.default_mac_addr = '00:13:95:00:00:00'
+        self.mac_address = '00:13:95:00:00:00'
         self._rest_support = None
 
         # Northbound and Southbound ports
@@ -450,9 +450,16 @@
                     device.reason = 'Setting up PM configuration'
                     self.adapter_agent.update_device(device)
 
-                    self.pm_metrics = OltPmMetrics(self, device, grouped=True, freq_override=False)
+                    kwargs = {
+                        'nni-ports': self.northbound_ports.values(),
+                        'pon-ports': self.southbound_ports.values()
+                    }
+                    self.pm_metrics = OltPmMetrics(self.adapter_agent, self.device_id,
+                                                   grouped=True, freq_override=False,
+                                                   **kwargs)
+
                     pm_config = self.pm_metrics.make_proto()
-                    self.log.info("initial-pm-config", pm_config=pm_config)
+                    self.log.debug("initial-pm-config", pm_config=pm_config)
                     self.adapter_agent.update_device_pm_config(pm_config, init=True)
 
                 except Exception as e:
@@ -460,14 +467,6 @@
                     self.activate_failed(device, e.message)
 
                 ############################################################################
-                # Setup Alarm handler
-
-                device.reason = 'Setting up Adapter Alarms'
-                self.adapter_agent.update_device(device)
-
-                self.alarms = AdapterAlarms(self.adapter, device.id)
-
-                ############################################################################
                 # Set the ports in a known good initial state
                 if not reconciling:
                     try:
@@ -494,6 +493,14 @@
                     self.activate_failed(device, e.message)
 
                 ############################################################################
+                # Setup Alarm handler
+
+                device.reason = 'Setting up Adapter Alarms'
+                self.adapter_agent.update_device(device)
+
+                self.alarms = AdapterAlarms(self.adapter_agent, device.id, ld_initialized.id)
+
+                ############################################################################
                 # Register for ONU detection
                 # self.adapter_agent.register_for_onu_detect_state(device.id)
 
@@ -520,7 +527,7 @@
                 self.logical_device_id = ld_initialized.id
 
                 # Start collecting stats from the device after a brief pause
-                reactor.callLater(10, self.start_kpi_collection, device.id)
+                reactor.callLater(10, self.pm_metrics.start_collector)
 
                 # Signal completion
                 self.log.info('activated')
@@ -651,7 +658,7 @@
             root_device_id=device.id)
 
         ld_initialized = self.adapter_agent.create_logical_device(ld,
-                                                                  dpid=self.default_mac_addr)
+                                                                  dpid=self.mac_address)
         return ld_initialized
 
     @inlineCallbacks
@@ -699,6 +706,9 @@
             from flow.flow_entry import FlowEntry
             FlowEntry.clear_all(device.id)
 
+            from download import Download
+            Download.clear_all(self.netconf_client)
+
         # Start/stop the interfaces as needed. These are deferred calls
 
         dl = []
@@ -1215,33 +1225,6 @@
         self.log.info('update_pm_config', pm_config=pm_config)
         self.pm_metrics.update(pm_config)
 
-    def start_kpi_collection(self, device_id):
-        # TODO: This has not been tested
-        def _collect(device_id, prefix):
-            from voltha.protos.events_pb2 import KpiEvent, KpiEventType, MetricValuePairs
-
-            try:
-                # Step 1: gather metrics from device
-                port_metrics = self.pm_metrics.collect_port_metrics()
-
-                # Step 2: prepare the KpiEvent for submission
-                # we can time-stamp them here or could use time derived from OLT
-                ts = arrow.utcnow().timestamp
-                kpi_event = KpiEvent(
-                    type=KpiEventType.slice,
-                    ts=ts,
-                    prefixes={
-                        prefix + '.{}'.format(k): MetricValuePairs(metrics=port_metrics[k])
-                        for k in port_metrics.keys()}
-                )
-                # Step 3: submit
-                self.adapter_agent.submit_kpis(kpi_event)
-
-            except Exception as e:
-                self.log.exception('failed-to-submit-kpis', e=e)
-
-        self.pm_metrics.start_collector(_collect)
-
     @inlineCallbacks
     def get_device_info(self, device):
         """
@@ -1285,7 +1268,7 @@
         device = self.adapter_agent.get_device(self.device_id)
 
         try:
-            from alarms.heartbeat_alarm import HeartbeatAlarm
+            from voltha.extensions.alarms.heartbeat_alarm import HeartbeatAlarm
 
             if self.heartbeat_miss >= self.heartbeat_failed_limit:
                 if device.connect_status == ConnectStatus.REACHABLE:
@@ -1294,7 +1277,7 @@
                     device.oper_status = OperStatus.FAILED
                     device.reason = self.heartbeat_last_reason
                     self.adapter_agent.update_device(device)
-                    HeartbeatAlarm(self, 'olt', self.heartbeat_miss).raise_alarm()
+                    HeartbeatAlarm(self.alarms, 'olt', self.heartbeat_miss).raise_alarm()
                     self.on_heatbeat_alarm(True)
             else:
                 # Update device states
@@ -1303,7 +1286,7 @@
                     device.oper_status = OperStatus.ACTIVE
                     device.reason = ''
                     self.adapter_agent.update_device(device)
-                    HeartbeatAlarm(self, 'olt').clear_alarm()
+                    HeartbeatAlarm(self.alarms, 'olt').clear_alarm()
                     self.on_heatbeat_alarm(False)
 
                 if self.netconf_client is None or not self.netconf_client.connected:
diff --git a/voltha/adapters/adtran_olt/adtran_olt.py b/voltha/adapters/adtran_olt/adtran_olt.py
index 9b846f0..6006755 100644
--- a/voltha/adapters/adtran_olt/adtran_olt.py
+++ b/voltha/adapters/adtran_olt/adtran_olt.py
@@ -41,7 +41,8 @@
         DeviceType(
             id=name,
             adapter=name,
-            accepts_bulk_flow_update=True
+            accepts_bulk_flow_update=True,
+            accepts_add_remove_flow_updates=False       # TODO: Support flow-mods
         )
     ]
 
@@ -50,8 +51,8 @@
         self.config = config
         self.descriptor = Adapter(
             id=self.name,
-            vendor='Adtran, Inc.',
-            version='0.16',
+            vendor='Adtran Inc.',
+            version='0.17',
             config=AdapterConfig(log_level=LogLevel.INFO)
         )
         log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
diff --git a/voltha/adapters/adtran_olt/adtran_olt_handler.py b/voltha/adapters/adtran_olt/adtran_olt_handler.py
index 3b334e8..3864c9b 100644
--- a/voltha/adapters/adtran_olt/adtran_olt_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_olt_handler.py
@@ -225,8 +225,9 @@
             command = "ip -o link | grep eth0 | sed -n -e 's/^.*ether //p' | awk '{ print $1 }'"
             rcmd = RCmd(self.ip_address, self.netconf_username, self.netconf_password,
                         command)
-            self.default_mac_addr = yield rcmd.execute()
-            self.log.info("mac-addr", mac_addr=self.default_mac_addr)
+            address = yield rcmd.execute()
+            self.mac_address = address.replace('\n', '')
+            self.log.info("mac-addr", mac_addr=self.mac_address)
 
         except Exception as e:
             log.exception('mac-address', e=e)
diff --git a/voltha/adapters/adtran_olt/alarms/__init__.py b/voltha/adapters/adtran_olt/alarms/__init__.py
deleted file mode 100644
index b0fb0b2..0000000
--- a/voltha/adapters/adtran_olt/alarms/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2017-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.
diff --git a/voltha/adapters/adtran_olt/alarms/adapter_alarms.py b/voltha/adapters/adtran_olt/alarms/adapter_alarms.py
deleted file mode 100644
index d74f726..0000000
--- a/voltha/adapters/adtran_olt/alarms/adapter_alarms.py
+++ /dev/null
@@ -1,133 +0,0 @@
-#
-# Copyright 2017 the original author or authors.
-#
-# 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.
-
-import structlog
-import arrow
-from voltha.protos.events_pb2 import AlarmEventType, \
-    AlarmEventSeverity, AlarmEventState, AlarmEventCategory
-
-# TODO: In the device adapter, the following alarms are still TBD
-#       (Taken from microsemi, so mileage may vare
-# ON_ALARM_SOFTWARE_ERROR = 0
-# PON_ALARM_LOS = 1
-# PON_ALARM_LOSI = 2
-# PON_ALARM_DOWI = 3
-# PON_ALARM_LOFI = 4
-# PON_ALARM_RDII = 5
-# PON_ALARM_LOAMI = 6
-# PON_ALARM_LCDGI = 7
-# PON_ALARM_LOAI = 8
-# PON_ALARM_SDI = 9
-# PON_ALARM_SFI = 10
-# PON_ALARM_PEE = 11
-# PON_ALARM_DGI = 12
-# PON_ALARM_LOKI = 13
-# PON_ALARM_TIWI = 14
-# PON_ALARM_TIA = 15
-# PON_ALARM_VIRTUAL_SCOPE_ONU_LASER_ALWAYS_ON = 16
-# PON_ALARM_VIRTUAL_SCOPE_ONU_SIGNAL_DEGRADATION = 17
-# PON_ALARM_VIRTUAL_SCOPE_ONU_EOL = 18
-# PON_ALARM_VIRTUAL_SCOPE_ONU_EOL_DATABASE_IS_FULL = 19
-# PON_ALARM_AUTH_FAILED_IN_REGISTRATION_ID_MODE = 20
-# PON_ALARM_SUFI = 21
-
-
-class AdapterAlarms:
-    def __init__(self, adapter, device_id):
-        self.log = structlog.get_logger(device_id=device_id)
-        self.adapter = adapter
-        self.device_id = device_id
-        self.lc = None
-
-    def format_id(self, alarm):
-        return 'voltha.{}.{}.{}'.format(self.adapter.name,
-                                        self.device_id,
-                                        alarm)
-
-    def format_description(self, _object, alarm, status):
-        return '{} Alarm - {} - {}'.format(_object.upper(),
-                                           alarm.upper(),
-                                           'Raised' if status else 'Cleared')
-
-    def send_alarm(self, context_data, alarm_data):
-        try:
-            current_context = {}
-
-            if isinstance(context_data, dict):
-                for key, value in context_data.iteritems():
-                    current_context[key] = str(value)
-
-            alarm_event = self.adapter.adapter_agent.create_alarm(
-                id=alarm_data.get('id', 'voltha.{}.{}.olt'.format(self.adapter.name,
-                                                                  self.device_id)),
-                resource_id=str(alarm_data.get('resource_id', self.device_id)),
-                description="{}.{} - {}".format(self.adapter.name, self.device_id,
-                                                alarm_data.get('description')),
-                type=alarm_data.get('type'),
-                category=alarm_data.get('category'),
-                severity=alarm_data.get('severity'),
-                state=alarm_data.get('state'),
-                raised_ts=alarm_data.get('ts', 0),
-                context=current_context
-            )
-            self.adapter.adapter_agent.submit_alarm(self.device_id, alarm_event)
-
-        except Exception as e:
-            self.log.exception('failed-to-send-alarm', e=e)
-
-
-class AlarmBase(object):
-    def __init__(self, handler, object_type, alarm,
-                 alarm_category,
-                 resource_id=None,
-                 alarm_type=AlarmEventType.EQUIPMENT,
-                 alarm_severity=AlarmEventSeverity.CRITICAL):
-
-        self._handler = handler
-        self._object_type = object_type
-        self._alarm = alarm
-        self._alarm_category = alarm_category
-        self._alarm_type = alarm_type
-        self._alarm_severity = alarm_severity
-        self._resource_id = resource_id
-
-    def get_alarm_data(self, status):
-        data = {
-            'ts': arrow.utcnow().timestamp,
-            'description': self._handler.alarms.format_description(self._object_type,
-                                                                   self._alarm,
-                                                                   status),
-            'id': self._handler.alarms.format_id(self._alarm),
-            'type': self._alarm_type,
-            'category': self._alarm_category,
-            'severity': self._alarm_severity,
-            'state': AlarmEventState.RAISED if status else AlarmEventState.CLEARED
-        }
-        if self._resource_id is not None:
-            data['resource_id'] = self._resource_id
-        return data
-
-    def get_context_data(self):
-        return {}   # You should override this if needed
-
-    def raise_alarm(self):
-        alarm_data = self.get_alarm_data(True)
-        context_data = self.get_context_data()
-        self._handler.alarms.send_alarm(context_data, alarm_data)
-
-    def clear_alarm(self):
-        alarm_data = self.get_alarm_data(False)
-        context_data = self.get_context_data()
-        self._handler.alarms.send_alarm(context_data, alarm_data)
diff --git a/voltha/adapters/adtran_olt/alarms/heartbeat_alarm.py b/voltha/adapters/adtran_olt/alarms/heartbeat_alarm.py
deleted file mode 100644
index 418867f..0000000
--- a/voltha/adapters/adtran_olt/alarms/heartbeat_alarm.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# 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.
-from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
-from adapter_alarms import AlarmBase
-
-
-class HeartbeatAlarm(AlarmBase):
-    def __init__(self, handler, object_type='olt', heartbeat_misses=0):
-        super(HeartbeatAlarm, self).__init__(handler, object_type,
-                                             alarm='Heartbeat',
-                                             alarm_category=AlarmEventCategory.PON,
-                                             alarm_type=AlarmEventType.EQUIPMENT,
-                                             alarm_severity=AlarmEventSeverity.CRITICAL)
-        self._misses = heartbeat_misses
-
-    def get_context_data(self):
-        return {'heartbeats-missed': self._misses}
-
diff --git a/voltha/adapters/adtran_olt/alarms/onu_active_alarm.py b/voltha/adapters/adtran_olt/alarms/onu_active_alarm.py
deleted file mode 100644
index 61e6825..0000000
--- a/voltha/adapters/adtran_olt/alarms/onu_active_alarm.py
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# 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.
-
-from adapter_alarms import AlarmBase
-from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
-
-
-class OnuActiveAlarm(AlarmBase):
-    def __init__(self, handler, pon_id, serial_number, reg_id):
-        super(OnuActiveAlarm, self).__init__(handler, 'ONU',
-                                             alarm='ONU_ACTIVATED',
-                                             alarm_category=AlarmEventCategory.PON,
-                                             resource_id=pon_id,
-                                             alarm_type=AlarmEventType.EQUIPMENT,
-                                             alarm_severity=AlarmEventSeverity.CRITICAL)
-        self._pon_id = pon_id
-        self._serial_number = serial_number
-        self._device_id = handler.device_id
-        device = handler.adapter_agent.get_device(handler.device_id)
-        self._olt_serial_number = device.serial_number
-        self._host = device.ipv4_address
-        self._datapath_id = device.parent_id
-        self._reg_id = reg_id
-
-    def get_context_data(self):
-        return {
-            'pon-id': self._pon_id,
-            'serial-number': self._serial_number,
-            'host': self._host,
-            'olt_serial_number': self._olt_serial_number,
-            'datapath_id': self._datapath_id,
-            'device_id' : self._device_id,
-            'registration_id' : self._reg_id
-        }
-
-    def clear_alarm(self):
-        raise NotImplementedError('ONU Active Alarms are auto-clear')
-
diff --git a/voltha/adapters/adtran_olt/alarms/onu_discovery_alarm.py b/voltha/adapters/adtran_olt/alarms/onu_discovery_alarm.py
deleted file mode 100644
index c9dd527..0000000
--- a/voltha/adapters/adtran_olt/alarms/onu_discovery_alarm.py
+++ /dev/null
@@ -1,36 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# 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.
-from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
-from adapter_alarms import AlarmBase
-
-
-class OnuDiscoveryAlarm(AlarmBase):
-    def __init__(self, handler, pon_id, serial_number):
-        super(OnuDiscoveryAlarm, self).__init__(handler, 'ONU Discovery',
-                                                alarm='Discovery',
-                                                alarm_category=AlarmEventCategory.PON,
-                                                resource_id=pon_id,
-                                                alarm_type=AlarmEventType.EQUIPMENT,
-                                                alarm_severity=AlarmEventSeverity.CRITICAL)
-        self._pon_id = pon_id
-        self._serial_number = serial_number
-
-    def get_context_data(self):
-        return {
-            'pon-id': self._pon_id,
-            'serial-number': self._serial_number
-        }
-
-    def clear_alarm(self):
-        raise NotImplementedError('ONU Discovery Alarms are auto-clear')
diff --git a/voltha/adapters/adtran_olt/alarms/onu_los_alarm.py b/voltha/adapters/adtran_olt/alarms/onu_los_alarm.py
deleted file mode 100644
index fba03a6..0000000
--- a/voltha/adapters/adtran_olt/alarms/onu_los_alarm.py
+++ /dev/null
@@ -1,29 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# 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.
-from voltha.protos.events_pb2 import AlarmEventType, AlarmEventSeverity, AlarmEventCategory
-from adapter_alarms import AlarmBase
-
-
-class OnuLosAlarm(AlarmBase):
-    def __init__(self, handler, onu_id):
-        super(OnuLosAlarm, self).__init__(handler, 'onu LOS',
-                                          alarm='LOS',
-                                          alarm_category=AlarmEventCategory.ONT,
-                                          alarm_type=AlarmEventType.COMMUNICATION,
-                                          alarm_severity=AlarmEventSeverity.MAJOR)
-        self._onu_id = onu_id
-
-    def get_context_data(self):
-        return {'onu-id': self._onu_id}
-
diff --git a/voltha/adapters/adtran_olt/download.py b/voltha/adapters/adtran_olt/download.py
index cbd3053..2639bdc 100644
--- a/voltha/adapters/adtran_olt/download.py
+++ b/voltha/adapters/adtran_olt/download.py
@@ -22,6 +22,10 @@
 
 log = structlog.get_logger()
 
+# TODO: Following two would be good provisionable parameters
+DEFAULT_AUTO_AGE_MINUTES = 10
+DEFAULT_MAX_JOB_RUN_SECONDS = 3600 * 4     # Some OLT files are 250MB+
+
 
 class Download(object):
     """Class to wrap an image download"""
@@ -55,6 +59,9 @@
         # Download job info
         self._download_job_name = None
 
+        self._age_out_period = DEFAULT_AUTO_AGE_MINUTES
+        self._max_execution = DEFAULT_MAX_JOB_RUN_SECONDS
+
     def __str__(self):
         return "ImageDownload: {}".format(self.name)
 
@@ -64,8 +71,8 @@
         Create and start a new image download
 
         :param handler: (AdtranDeviceHandler) Device download is for
-        :param done_deferred: (Deferred) deferred to fire on completion
         :param request: (ImageDownload) Request
+        :param supported_protocols: (list) download methods allowed (http, tftp, ...)
         """
         download = Download(handler, request, supported_protocols)
         download._deferred = reactor.callLater(0, download.start_download)
@@ -238,27 +245,37 @@
 
     @property
     def download_job_xml(self):
-        filepath = os.path.split(self._path)
-
+        # TODO: May want to support notifications
+        # TODO: Not sure about this name for the entity
+        entity = 'main 0'
         xml = """
               <maintenance-jobs xmlns="http://www.adtran.com/ns/yang/adtran-maintenance-jobs" xmlns:adtn-phys-sw-mnt="http://www.adtran.com/ns/yang/adtran-physical-software-maintenance">
                 <maintenance-job>
                   <name>{}</name>
                   <enabled>true</enabled>
                   <notify-enabled>false</notify-enabled>
-                  <no-execution-time-limit/>
+                  <maximum-execution-time>{}</maximum-execution-time>
                   <run-once>true</run-once>
                   <adtn-phys-sw-mnt:download-software>
+                    <adtn-phys-sw-mnt:physical-entity>{}</adtn-phys-sw-mnt:physical-entity>
+                    <adtn-phys-sw-mnt:software-name>software</adtn-phys-sw-mnt:software-name>
                     <adtn-phys-sw-mnt:remote-file>
                       <adtn-phys-sw-mnt:file-server-profile>{}</adtn-phys-sw-mnt:file-server-profile>
-                      <adtn-phys-sw-mnt:filepath>{}</adtn-phys-sw-mnt:filepath>
                       <adtn-phys-sw-mnt:filename>{}</adtn-phys-sw-mnt:filename>
+        """.format(self._download_job_name, self._max_execution, entity,
+                   self._server_profile_name, self._name)
+
+        if self._path is not None:
+            xml += """
+                          <adtn-phys-sw-mnt:filepath>{}</adtn-phys-sw-mnt:filepath>
+                """.format(self._path)
+
+        xml += """
                     </adtn-phys-sw-mnt:remote-file>
                   </adtn-phys-sw-mnt:download-software>
                 </maintenance-job>
               </maintenance-jobs>
-        """.format(self._download_job_name, self._server_profile_name,
-                   filepath[0], filepath[1])
+        """
         return xml
 
     @property
@@ -277,25 +294,25 @@
     @property
     def delete_server_profile_xml(self):
         xml = """
-        <file-servers operation="delete" xmlns="http://www.adtran.com/ns/yang/adtran-file-servers">
-          <profiles>
+        <file-servers xmlns="http://www.adtran.com/ns/yang/adtran-file-servers">
+          <profiles operation="delete">
             <profile>
               <name>{}</name>
             </profile>
            </profiles>
         </file-servers>
-        """.format(self._name)
+        """.format(self._server_profile_name)
         return xml
 
     @property
     def delete_download_job_xml(self):
         xml = """
-        <maintenance-jobs operation="delete" xmlns="http://www.adtran.com/ns/yang/adtran-maintenance-jobs">
-          <maintenance-job>
+        <maintenance-jobs xmlns="http://www.adtran.com/ns/yang/adtran-maintenance-jobs">
+          <maintenance-job operation="delete">>
             <name>{}</name>
           </maintenance-job>
         </maintenance-jobs>
-        """.format(self._name)
+        """.format(self._download_job_name)
         return xml
 
     @inlineCallbacks
@@ -352,7 +369,6 @@
         self._download_state = ImageDownload.DOWNLOAD_FAILED
 
         # Cleanup NETCONF
-
         reactor.callLater(0, self._cleanup_download_job, 20)
         reactor.callLater(0, self._cleanup_server_profile, 20)
         # TODO: Do we signal any completion due to failure?
@@ -375,7 +391,6 @@
             device.admin_state = AdminState.ENABLED
             self._handler.adapter_agent.update_device(device)
 
-    @inlineCallbacks
     def cancel_download(self, request):
         log.info('cancel-sw-download', name=self.name)
 
@@ -466,7 +481,7 @@
             'invalid-software': ImageDownload.DOWNLOAD_FAILED,            # successfully downloaded the required software but the software was determined to be invalid
             'software-storage-failed': ImageDownload.INSUFFICIENT_SPACE,  # successfully downloaded the required software but was unable to successfully stored it to memory
         }.get(state.lower(), None)
-        log.info('download-state', result=result, state=state, name=self.name)
+        log.info('download-software-state', result=result, state=state, name=self.name)
         assert result is not None, 'Invalid state'
         return result
 
@@ -484,6 +499,26 @@
             'software-committed': ImageDownload.IMAGE_ACTIVATE,        # successfully committed the required software. The job terminated successfully
             'commit-software-failed': ImageDownload.IMAGE_INACTIVE,    # unsuccessfully attempted to commit the required software revision
         }.get(state.lower(), None)
-        log.info('download-state', result=result, state=state, name=self.name)
+        log.info('download-activate-state', result=result, state=state, name=self.name)
         assert result is not None, 'Invalid state'
-        return result
\ No newline at end of file
+        return result
+
+    @staticmethod
+    def clear_all(client):
+        """
+        Remove all file server profiles and download jobs
+        :param client: (ncclient) NETCONF Client to use
+        """
+        from twisted.internet import defer
+        del_fs_xml = """
+            <file-servers xmlns="http://www.adtran.com/ns/yang/adtran-file-servers">
+              <profiles operation="delete"/>
+            </file-servers>
+            """
+        del_job_xml = """
+            <maintenance-jobs operation="delete" xmlns="http://www.adtran.com/ns/yang/adtran-maintenance-jobs"/>
+            """
+        dl = [client.edit_config(del_fs_xml),
+              client.edit_config(del_job_xml)]
+
+        return defer.gatherResults(dl, consumeErrors=True)
diff --git a/voltha/adapters/adtran_olt/flow/evc_map.py b/voltha/adapters/adtran_olt/flow/evc_map.py
index 239113e..9c5cbe3 100644
--- a/voltha/adapters/adtran_olt/flow/evc_map.py
+++ b/voltha/adapters/adtran_olt/flow/evc_map.py
@@ -736,7 +736,7 @@
           </evc-maps>
         </filter>
         """
-        log.info('query', xml=get_xml, regex=regex_)
+        log.debug('query', xml=get_xml, regex=regex_)
 
         def request_failed(results, operation):
             log.error('{}-failed'.format(operation), results=results)
diff --git a/voltha/adapters/adtran_olt/net/adtran_netconf.py b/voltha/adapters/adtran_olt/net/adtran_netconf.py
index 07518ea..7c4741a 100644
--- a/voltha/adapters/adtran_olt/net/adtran_netconf.py
+++ b/voltha/adapters/adtran_olt/net/adtran_netconf.py
@@ -302,14 +302,14 @@
                                                     config, default_operation,
                                                     test_option, error_option)
         except Exception as e:
-            log.exception('edit_config', e=e)
+            log.exception('edit_config', e=e, config=config, target=target)
             raise
 
         returnValue(rpc_reply)
 
     def _do_edit_config(self, target, config, default_operation, test_option, error_option):
         """
-        Lock the configuration system
+        Perform actual edit-config operation
         """
         try:
             log.debug('edit-config', target=target, config=config)
@@ -326,7 +326,7 @@
             # To check status, use response.ok  (boolean)
 
         except RPCError as e:
-            log.exception('do_edit_config', e=e)
+            log.exception('do_edit_config', e=e, config=config, target=target)
             raise
 
         return response
diff --git a/voltha/adapters/adtran_olt/onu.py b/voltha/adapters/adtran_olt/onu.py
index 4ec2cb3..2681d00 100644
--- a/voltha/adapters/adtran_olt/onu.py
+++ b/voltha/adapters/adtran_olt/onu.py
@@ -215,9 +215,13 @@
             # Must remove any non-printable characters
             reg_id = ''.join([i if ord(i) < 127 and ord(i) > 31 else '_' for i in reg_id])
             # Generate alarm here for regID
-            from alarms.onu_active_alarm import OnuActiveAlarm
+            from voltha.extensions.alarms.onu.onu_active_alarm import OnuActiveAlarm
             self.log.info('onu-Active-Alarm', serial_number=self._serial_number_string)
-            OnuActiveAlarm(self._olt, self._pon_id, self._serial_number_string, reg_id).raise_alarm()
+            device = self._olt.adapter_agent.get_device(self._olt.device_id)
+
+            OnuActiveAlarm(self._olt.alarms, self._olt.device_id, self._pon_id,
+                           self._serial_number_string, reg_id, device.serial_number,
+                           ipv4_address=device.ipv4_address).raise_alarm()
 
     @property
     def enabled(self):
diff --git a/voltha/adapters/adtran_olt/pki/__init__.py b/voltha/adapters/adtran_olt/pki/__init__.py
deleted file mode 100644
index b0fb0b2..0000000
--- a/voltha/adapters/adtran_olt/pki/__init__.py
+++ /dev/null
@@ -1,13 +0,0 @@
-# Copyright 2017-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.
diff --git a/voltha/adapters/adtran_olt/pki/adapter_pm_metrics.py b/voltha/adapters/adtran_olt/pki/adapter_pm_metrics.py
deleted file mode 100644
index 053350e..0000000
--- a/voltha/adapters/adtran_olt/pki/adapter_pm_metrics.py
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# 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.
-
-import structlog
-from twisted.internet.task import LoopingCall
-from voltha.protos.device_pb2 import PmConfig, PmConfigs, PmGroupConfig
-
-
-class AdapterPmMetrics(object):
-    def __init__(self, handler, device, grouped=False, freq_override=False):
-        self.log = structlog.get_logger(device_id=device.id)
-        self.device = device
-        self.id = device.id
-        self.handler = handler
-        self.name = handler.adapter.name
-        self.default_freq = 150
-        self.grouped = grouped
-        self.freq_override = grouped and freq_override
-        self.lc = None
-
-    def update(self, pm_config):
-        raise NotImplementedError('Your derived class should override this method')
-
-    # def enable_pm_collection(self, pm_group, remote):
-    #     if pm_group == 'Ethernet':
-    #         self.configure_pm_collection_freq(self.default_freq / 10, remote)
-    #
-    # def disable_pm_collection(self, pm_group, remote):
-    #     if pm_group == 'nni':
-    #         self.configure_pm_collection_freq(0, remote)
-
-    def make_proto(self):
-        raise NotImplementedError('Your derived class should override this method')
-
-    def start_collector(self, callback):
-        self.log.info("starting-pm-collection", device_name=self.name,
-                      device_id=self.device.id)
-        prefix = 'voltha.{}.{}'.format(self.name, self.device.id)
-
-        if self.lc is None:
-            self.lc = LoopingCall(callback, self.device.id, prefix)
-
-        self.lc.start(interval=self.default_freq / 10)
-
-    def stop_collector(self):
-        if self.lc is not None:
-            self.lc.stop()
-
-    def collect_metrics(self, group, names, config):
-        stats = {metric: getattr(group, metric) for (metric, t) in names}
-        return {metric: value for metric, value in stats.iteritems()
-                if config[metric].enabled}
diff --git a/voltha/adapters/adtran_olt/pki/olt_pm_metrics.py b/voltha/adapters/adtran_olt/pki/olt_pm_metrics.py
deleted file mode 100644
index 2d1fdf6..0000000
--- a/voltha/adapters/adtran_olt/pki/olt_pm_metrics.py
+++ /dev/null
@@ -1,242 +0,0 @@
-# Copyright 2017-present Adtran, Inc.
-#
-# 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.
-
-import structlog
-
-from voltha.protos.device_pb2 import PmConfig, PmConfigs, PmGroupConfig
-
-from adapter_pm_metrics import AdapterPmMetrics
-
-
-class OltPmMetrics(AdapterPmMetrics):
-    def __init__(self, handler, device, grouped=False, freq_override=False):
-        super(OltPmMetrics, self).__init__(handler, device,
-                                           grouped=grouped, freq_override=freq_override)
-
-        # PM Config Types are COUNTER, GUAGE, and STATE     # GAUGE is misspelled device.proto
-        self.nni_pm_names = {
-            ('admin_state', PmConfig.STATE),
-            ('oper_status', PmConfig.STATE),
-            ('port_no', PmConfig.GUAGE),  # Device and logical_device port numbers same
-            ('rx_packets', PmConfig.COUNTER),
-            ('rx_bytes', PmConfig.COUNTER),
-            ('rx_dropped', PmConfig.COUNTER),
-            ('rx_errors', PmConfig.COUNTER),
-            ('rx_bcast', PmConfig.COUNTER),
-            ('rx_mcast', PmConfig.COUNTER),
-            ('tx_packets', PmConfig.COUNTER),
-            ('tx_bytes', PmConfig.COUNTER),
-            ('tx_dropped', PmConfig.COUNTER),
-            ('tx_bcast', PmConfig.COUNTER),
-            ('tx_mcast', PmConfig.COUNTER),
-            #
-            # Commented out are from spec. May not be supported or implemented yet
-            # ('rx_64', PmConfig.COUNTER),
-            # ('rx_65_127', PmConfig.COUNTER),
-            # ('rx_128_255', PmConfig.COUNTER),
-            # ('rx_256_511', PmConfig.COUNTER),
-            # ('rx_512_1023', PmConfig.COUNTER),
-            # ('rx_1024_1518', PmConfig.COUNTER),
-            # ('rx_frame_err', PmConfig.COUNTER),
-            # ('rx_over_err', PmConfig.COUNTER),
-            # ('rx_crc_err', PmConfig.COUNTER),
-            # ('rx_64', PmConfig.COUNTER),
-            # ('tx_65_127', PmConfig.COUNTER),
-            # ('tx_128_255', PmConfig.COUNTER),
-            # ('tx_256_511', PmConfig.COUNTER),
-            # ('tx_512_1023', PmConfig.COUNTER),
-            # ('tx_1024_1518', PmConfig.COUNTER),
-            # ('collisions', PmConfig.COUNTER),
-        }
-        self.pon_pm_names = {
-            ('admin_state', PmConfig.STATE),
-            ('oper_status', PmConfig.STATE),
-            ('port_no', PmConfig.GUAGE),        # Physical device port number
-            ('pon_id', PmConfig.GUAGE),
-            ('rx_packets', PmConfig.COUNTER),
-            ('rx_bytes', PmConfig.COUNTER),
-            ('tx_packets', PmConfig.COUNTER),
-            ('tx_bytes', PmConfig.COUNTER),
-            ('tx_bip_errors', PmConfig.COUNTER),
-            ('in_service_onus', PmConfig.GUAGE),
-            ('closest_onu_distance', PmConfig.GUAGE)
-        }
-        self.onu_pm_names = {
-            ('pon_id', PmConfig.GUAGE),
-            ('onu_id', PmConfig.GUAGE),
-            ('fiber_length', PmConfig.GUAGE),
-            ('equalization_delay', PmConfig.GUAGE),
-            ('rssi', PmConfig.GUAGE),            #
-        }
-        self.gem_pm_names = {
-            ('pon_id', PmConfig.GUAGE),
-            ('onu_id', PmConfig.GUAGE),
-            ('gem_id', PmConfig.GUAGE),
-            ('alloc_id', PmConfig.GUAGE),
-            ('rx_packets', PmConfig.COUNTER),
-            ('rx_bytes', PmConfig.COUNTER),
-            ('tx_packets', PmConfig.COUNTER),
-            ('tx_bytes', PmConfig.COUNTER),
-        }
-        self.nni_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
-                                   for (m, t) in self.nni_pm_names}
-        self.pon_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
-                                   for (m, t) in self.pon_pm_names}
-        self.onu_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
-                                   for (m, t) in self.onu_pm_names}
-        self.gem_metrics_config = {m: PmConfig(name=m, type=t, enabled=True)
-                                   for (m, t) in self.gem_pm_names}
-
-    def update(self, pm_config):
-        # TODO: Test both 'group' and 'non-group' functionality
-        # TODO: Test frequency override capability for a particular group
-        if self.default_freq != pm_config.default_freq:
-            # Update the callback to the new frequency.
-            self.default_freq = pm_config.default_freq
-            self.lc.stop()
-            self.lc.start(interval=self.default_freq / 10)
-
-        if pm_config.grouped is True:
-            for m in pm_config.groups:
-                pass
-                # self.pm_group_metrics[m.group_name].config.enabled = m.enabled
-                # if m.enabled is True:
-                #     self.enable_pm_collection(m.group_name, remote)
-                # else:
-                #     self.disable_pm_collection(m.group_name, remote)
-        else:
-            for m in pm_config.metrics:
-                self.nni_metrics_config[m.name].enabled = m.enabled
-                self.pon_metrics_config[m.name].enabled = m.enabled
-                self.onu_metrics_config[m.name].enabled = m.enabled
-                self.gem_metrics_config[m.name].enabled = m.enabled
-
-    def make_proto(self):
-        pm_config = PmConfigs(id=self.id, default_freq=self.default_freq,
-                              grouped=self.grouped,
-                              freq_override=self.freq_override)
-        metrics = set()
-
-        if self.grouped:
-            pm_ether_stats = PmGroupConfig(group_name='Ethernet',
-                                           group_freq=self.default_freq,
-                                           enabled=True)
-
-            pm_pon_stats = PmGroupConfig(group_name='PON',
-                                         group_freq=self.default_freq,
-                                         enabled=True)
-
-            pm_ont_stats = PmGroupConfig(group_name='ONT',
-                                         group_freq=self.default_freq,
-                                         enabled=True)
-
-            pm_gem_stats = PmGroupConfig(group_name='GEM',
-                                         group_freq=self.default_freq,
-                                         enabled=True)
-        else:
-            pm_ether_stats = pm_config
-            pm_pon_stats = pm_config
-            pm_ont_stats = pm_config
-            pm_gem_stats = pm_config
-
-        for m in sorted(self.nni_metrics_config):
-            pm = self.nni_metrics_config[m]
-            if not self.grouped:
-                if pm.name in metrics:
-                    continue
-                metrics.add(pm.name)
-            pm_ether_stats.metrics.extend([PmConfig(name=pm.name,
-                                                    type=pm.type,
-                                                    enabled=pm.enabled)])
-
-        for m in sorted(self.pon_metrics_config):
-            pm = self.pon_metrics_config[m]
-            if not self.grouped:
-                if pm.name in metrics:
-                    continue
-                metrics.add(pm.name)
-            pm_pon_stats.metrics.extend([PmConfig(name=pm.name,
-                                                  type=pm.type,
-                                                  enabled=pm.enabled)])
-
-        for m in sorted(self.onu_metrics_config):
-            pm = self.onu_metrics_config[m]
-            if not self.grouped:
-                if pm.name in metrics:
-                    continue
-                metrics.add(pm.name)
-            pm_ont_stats.metrics.extend([PmConfig(name=pm.name,
-                                                  type=pm.type,
-                                                  enabled=pm.enabled)])
-
-        for m in sorted(self.gem_metrics_config):
-            pm = self.gem_metrics_config[m]
-            if not self.grouped:
-                if pm.name in metrics:
-                    continue
-                metrics.add(pm.name)
-            pm_gem_stats.metrics.extend([PmConfig(name=pm.name,
-                                                  type=pm.type,
-                                                  enabled=pm.enabled)])
-        if self.grouped:
-            pm_config.groups.extend([pm_ether_stats,
-                                     pm_pon_stats,
-                                     pm_ont_stats,
-                                     pm_gem_stats])
-        return pm_config
-
-    def collect_port_metrics(self):
-        port_metrics = dict()
-
-        for port in self.handler.northbound_ports.itervalues():
-            port_metrics['nni.{}'.format(port.port_no)] = self.collect_nni_metrics(port)
-
-        for port in self.handler.southbound_ports.itervalues():
-            port_metrics['pon.{}'.format(port.pon_id)] = self.collect_pon_metrics(port)
-
-            for onu_id in port.onu_ids:
-                onu = port.onu(onu_id)
-                if onu is not None:
-                    port_metrics['pon.{}.onu.{}'.format(port.pon_id, onu.onu_id)] = \
-                        self.collect_onu_metrics(onu)
-                    for gem in onu.gem_ports:
-                        if gem.multicast:
-                            continue
-
-                        port_metrics['pon.{}.onu.{}.gem.{}'.format(port.pon_id,
-                                                                   onu.onu_id,
-                                                                   gem.gem_id)] = \
-                            self.collect_gem_metrics(gem)
-            # TODO: Do any multicast GEM PORT metrics here...
-        return port_metrics
-
-    def collect_nni_metrics(self, nni_port):
-        stats = {metric: getattr(nni_port, metric) for (metric, t) in self.nni_pm_names}
-        return {metric: value for metric, value in stats.iteritems()
-                if self.nni_metrics_config[metric].enabled}
-
-    def collect_pon_metrics(self, pon_port):
-        stats = {metric: getattr(pon_port, metric) for (metric, t) in self.pon_pm_names}
-        return {metric: value for metric, value in stats.iteritems()
-                if self.pon_metrics_config[metric].enabled}
-
-    def collect_onu_metrics(self, onu):
-        stats = {metric: getattr(onu, metric) for (metric, t) in self.onu_pm_names}
-        return {metric: value for metric, value in stats.iteritems()
-                if self.onu_metrics_config[metric].enabled}
-
-    def collect_gem_metrics(self, gem):
-        stats = {metric: getattr(gem, metric) for (metric, t) in self.gem_pm_names}
-        return {metric: value for metric, value in stats.iteritems()
-                if self.gem_metrics_config[metric].enabled}
diff --git a/voltha/adapters/adtran_olt/pon_port.py b/voltha/adapters/adtran_olt/pon_port.py
index d0281ca..82dcd9b 100644
--- a/voltha/adapters/adtran_olt/pon_port.py
+++ b/voltha/adapters/adtran_olt/pon_port.py
@@ -18,20 +18,20 @@
 import structlog
 from port import AdtnPort
 from twisted.internet import reactor, defer
-from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from twisted.internet.defer import inlineCallbacks, returnValue
 
 from adtran_olt_handler import AdtranOltHandler
 from net.adtran_rest import RestInvalidResponseCode
 from codec.olt_config import OltConfig
 from onu import Onu
-from alarms.onu_los_alarm import OnuLosAlarm
+from voltha.extensions.alarms.onu.onu_los_alarm import OnuLosAlarm
 from voltha.protos.common_pb2 import AdminState
 from voltha.protos.device_pb2 import Port
 
 try:
-    from alarms.onu_discovery_alarm2 import OnuDiscoveryAlarm
+    from voltha.extensions.alarms.onu.onu_discovery_alarm import OnuDiscoveryAlarm
 except ImportError:
-    from alarms.onu_discovery_alarm import OnuDiscoveryAlarm
+    from voltha.extensions.alarms.onu.onu_discovery_alarm import OnuDiscoveryAlarm
 
 
 class PonPort(AdtnPort):
@@ -736,11 +736,11 @@
 
         for onu_id in cleared_alarms:
             self._active_los_alarms.remove(onu_id)
-            OnuLosAlarm(self.olt, onu_id).clear_alarm()
+            OnuLosAlarm(self.olt.alarms, onu_id).clear_alarm()
 
         for onu_id in new_alarms:
             self._active_los_alarms.add(onu_id)
-            OnuLosAlarm(self.olt, onu_id).raise_alarm()
+            OnuLosAlarm(self.olt.alarms, onu_id).raise_alarm()
             self.delete_onu(onu_id)
 
     def _process_status_onu_discovered_list(self, discovered_onus):
@@ -851,8 +851,9 @@
         onu_info = self._get_onu_info(serial_number)
 
         if onu_info is None:
-            self.log.info('onu-lookup-failure', serial_number=serial_number_64)
-            OnuDiscoveryAlarm(self.olt, self.pon_id, serial_number).raise_alarm()
+            self.log.info('onu-lookup-failure', serial_number=serial_number,
+                          serial_number_64=serial_number_64)
+            OnuDiscoveryAlarm(self.olt.alarms, self.pon_id, serial_number).raise_alarm()
             returnValue('new-onu')
 
         if serial_number_64 not in status.onus or onu_info['onu-id'] in self._active_los_alarms:
@@ -866,7 +867,7 @@
             elif (serial_number_64 in self._onus and onu_id not in self._onu_by_id) or \
                     (serial_number_64 not in self._onus and onu_id in self._onu_by_id):
                 # May be here due to unmanaged power-cycle on OLT or fiber bounced for a
-                # previously activated ONU. Drop it and add bac on next discovery cycle
+                # previously activated ONU. Drop it and add back on next discovery cycle
                 self.delete_onu(onu_id)
 
             elif len(self._onus) >= self.MAX_ONUS_SUPPORTED:
diff --git a/voltha/adapters/adtran_olt/port.py b/voltha/adapters/adtran_olt/port.py
index 5a0a025..69597a7 100644
--- a/voltha/adapters/adtran_olt/port.py
+++ b/voltha/adapters/adtran_olt/port.py
@@ -12,21 +12,12 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import json
-import random
-
 import structlog
 from enum import Enum
-from twisted.internet import reactor, defer
+from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 
-from adtran_olt_handler import AdtranOltHandler
-from net.adtran_rest import RestInvalidResponseCode
-from codec.olt_config import OltConfig
-from onu import Onu
-from alarms.onu_los_alarm import OnuLosAlarm
 from voltha.protos.common_pb2 import OperStatus, AdminState
-from voltha.protos.device_pb2 import Port
 
 
 class AdtnPort(object):