VOL-712: Reboot OMCI Task & Create/Set Tasks for Enable, Disable support
Change-Id: I95253b0f5b6f1c5080ddc36b02ec61e2414051f9
diff --git a/voltha/extensions/omci/me_frame.py b/voltha/extensions/omci/me_frame.py
index c713bc2..7ce812f 100644
--- a/voltha/extensions/omci/me_frame.py
+++ b/voltha/extensions/omci/me_frame.py
@@ -222,19 +222,21 @@
attributes_mask=self.entity_class.mask_for(*mask_set)
))
- def reboot(self):
+ def reboot(self, reboot_code=0):
"""
Create a Reboot request from for this ME
:return: (OmciFrame) OMCI Frame
"""
self._check_operation(OP.Reboot)
+ assert 0 <= reboot_code <= 2, 'Reboot code must be 0..2'
return OmciFrame(
transaction_id=None,
message_type=OmciReboot.message_id,
omci_message=OmciReboot(
entity_class=getattr(self.entity_class, 'class_id'),
- entity_id=getattr(self, 'entity_id')
+ entity_id=getattr(self, 'entity_id'),
+ reboot_code=reboot_code
))
def mib_reset(self):
diff --git a/voltha/extensions/omci/omci_cc.py b/voltha/extensions/omci/omci_cc.py
index 6cc05c4..db39abe 100644
--- a/voltha/extensions/omci/omci_cc.py
+++ b/voltha/extensions/omci/omci_cc.py
@@ -648,6 +648,8 @@
def send_reboot(self, timeout=DEFAULT_OMCI_TIMEOUT):
"""
Send an ONU Device reboot request (ONU-G ME).
+
+ NOTICE: This method is being deprecated and replaced with a tasks to preform this function
"""
self.log.debug('send-mib-reboot')
diff --git a/voltha/extensions/omci/onu_device_entry.py b/voltha/extensions/omci/onu_device_entry.py
index af60d15..02d7419 100644
--- a/voltha/extensions/omci/onu_device_entry.py
+++ b/voltha/extensions/omci/onu_device_entry.py
@@ -21,6 +21,9 @@
from common.event_bus import EventBusClient
from voltha.extensions.omci.tasks.task_runner import TaskRunner
from voltha.extensions.omci.onu_configuration import OnuConfiguration
+from voltha.extensions.omci.tasks.reboot_task import OmciRebootRequest, RebootFlags
+from voltha.extensions.omci.tasks.omci_modify_request import OmciModifyRequest
+from voltha.extensions.omci.omci_me import OntGFrame
from twisted.internet import reactor
from enum import IntEnum
@@ -291,6 +294,14 @@
# Start up the ONU Capabilities task
self._configuration.reset()
+ # Insure that the ONU-G Administrative lock is disabled
+ def failure(reason):
+ self.log.error('disable-admin-state-lock', reason=reason)
+
+ frame = OntGFrame(attributes={'administrative_state': 0}).set()
+ task = OmciModifyRequest(self._omci_agent, self.device_id, frame)
+ self.task_runner.queue_task(task).addErrback(failure)
+
# Start up any other remaining OpenOMCI state machines
def start_state_machines(machines):
for sm in machines:
@@ -379,3 +390,20 @@
attributes=attribute)
return entry[attribute] if attribute in entry else None
+
+ def reboot(self,
+ flags=RebootFlags.Reboot_Unconditionally,
+ timeout=OmciRebootRequest.DEFAULT_PRIORITY):
+ """
+ Request a reboot of the ONU
+
+ :param flags: (RebootFlags) Reboot condition
+ :param timeout: (int) Reboot task priority
+ :return: (deferred) Fires upon completion or error
+ """
+ assert self.active, 'This device is not active'
+
+ return self.task_runner.queue_task(OmciRebootRequest(self._omci_agent,
+ self.device_id,
+ flags=flags,
+ timeout=timeout))
diff --git a/voltha/extensions/omci/tasks/omci_get_request.py b/voltha/extensions/omci/tasks/omci_get_request.py
index 7d12829..137f50c 100644
--- a/voltha/extensions/omci/tasks/omci_get_request.py
+++ b/voltha/extensions/omci/tasks/omci_get_request.py
@@ -88,7 +88,7 @@
def stop_if_not_running(self):
if not self.running:
- raise MibResyncException('Get Request Task was cancelled')
+ raise GetException('Get Request Task was cancelled')
@property
def attributes(self):
diff --git a/voltha/extensions/omci/tasks/omci_modify_request.py b/voltha/extensions/omci/tasks/omci_modify_request.py
new file mode 100644
index 0000000..de15011
--- /dev/null
+++ b/voltha/extensions/omci/tasks/omci_modify_request.py
@@ -0,0 +1,176 @@
+#
+# Copyright 2018 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.
+#
+from task import Task
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, failure, returnValue
+from voltha.extensions.omci.omci_defs import ReasonCodes, EntityOperations
+from voltha.extensions.omci.omci_me import MEFrame
+from voltha.extensions.omci.omci_frame import OmciFrame
+from voltha.extensions.omci.omci_messages import OmciCreate, OmciSet, OmciDelete
+from voltha.extensions.omci.omci_entities import EntityClass
+
+RC = ReasonCodes
+OP = EntityOperations
+
+
+class ModifyException(Exception):
+ pass
+
+
+class OmciModifyRequest(Task):
+ """
+ OpenOMCI Generic Create, Set, or Delete Frame support Task.
+
+ This task allows an ONU to send a Create, Set, or Delete request from any point in their
+ code while properly using the OMCI-CC channel. Direct access to the OMCI-CC object
+ to send requests by an ONU is highly discouraged.
+ """
+ task_priority = 128
+ name = "ONU OMCI Modify Task"
+
+ def __init__(self, omci_agent, device_id, frame, priority=task_priority, exclusive=False):
+ """
+ Class initialization
+
+ :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+ :param device_id: (str) ONU Device ID
+ :param frame: (OmciFrame) Frame to send
+ :param priority: (int) OpenOMCI Task priority (0..255) 255 is the highest
+ :param exclusive: (bool) True if this GET request Task exclusively own the
+ OMCI-CC while running. Default: False
+ """
+ super(OmciModifyRequest, self).__init__(OmciModifyRequest.name,
+ omci_agent,
+ device_id,
+ priority=priority,
+ exclusive=exclusive)
+ self._device = omci_agent.get_device(device_id)
+ self._frame = frame
+ self._results = None
+ self._local_deferred = None
+
+ # Validate message type
+ self._msg_type = frame.fields['message_type']
+ if self._msg_type not in (OmciCreate.message_id, OmciSet.message_id, OmciDelete.message_id):
+ raise TypeError('Invalid Message type: {}, must be Create, Set, or Delete'.
+ format(self._msg_type))
+
+ def cancel_deferred(self):
+ super(OmciModifyRequest, self).cancel_deferred()
+
+ d, self._local_deferred = self._local_deferred, None
+ try:
+ if d is not None and not d.called:
+ d.cancel()
+ except:
+ pass
+
+ def stop_if_not_running(self):
+ if not self.running:
+ raise ModifyException('Modify Request Task was cancelled')
+
+ @property
+ def success_code(self):
+ """
+ Return the OMCI success/reason code for the Get Response.
+ """
+ if self._results is None:
+ return None
+
+ return self._results.fields['omci_message'].fields['success_code']
+
+ @property
+ def illegal_attributes_mask(self):
+ """
+ For Create & Set requests, a failure may indicate that one or more
+ attributes have an illegal value. This property returns any illegal
+ attributes
+
+ :return: None if not a create/set request, otherwise the attribute mask
+ of illegal attributes
+ """
+ if self._results is None:
+ return None
+
+ omci_msg = self._results.fields['omci_message'].fields
+
+ if self._msg_type == OmciCreate.message_id:
+ if self.success_code != RC.ParameterError:
+ return 0
+ return omci_msg['parameter_error_attributes_mask']
+
+ elif self._msg_type == OmciSet.message_id:
+ if self.success_code != RC.AttributeFailure:
+ return 0
+ return omci_msg['failed_attributes_mask']
+
+ return None
+
+ @property
+ def unsupported_attributes_mask(self):
+ """
+ For Set requests, a failure may indicate that one or more attributes
+ are not supported by this ONU. This property returns any those unsupported attributes
+ attributes
+
+ :return: None if not a set request, otherwise the attribute mask of any illegal
+ parameters
+ """
+ if self._msg_type != OmciSet.message_id or self._results is None:
+ return None
+
+ if self.success_code != RC.AttributeFailure:
+ return 0
+
+ return self._results.fields['omci_message'].fields['unsupported_attributes_mask']
+
+ @property
+ def raw_results(self):
+ """
+ Return the raw Response OMCIFrame
+ """
+ return self._results
+
+ def start(self):
+ """
+ Start MIB Capabilities task
+ """
+ super(OmciModifyRequest, self).start()
+ self._local_deferred = reactor.callLater(0, self.perform_omci)
+
+ @inlineCallbacks
+ def perform_omci(self):
+ """
+ Perform the request
+ """
+ self.log.info('perform-request')
+
+ try:
+ self._results = yield self._device.omci_cc.send(self._frame)
+ self.stop_if_not_running()
+
+ status = self._results.fields['omci_message'].fields['success_code']
+ self.log.info('response-status', status=status)
+
+ # Success?
+ if status in (RC.Success.value, RC.InstanceExists):
+ self.deferred.callback(self)
+ else:
+ raise ModifyException('Failed with status {}'.format(status))
+
+ except Exception as e:
+ self.log.exception('perform-modify', e=e)
+ self.deferred.errback(failure.Failure(e))
diff --git a/voltha/extensions/omci/tasks/reboot_task.py b/voltha/extensions/omci/tasks/reboot_task.py
new file mode 100644
index 0000000..ba4c290
--- /dev/null
+++ b/voltha/extensions/omci/tasks/reboot_task.py
@@ -0,0 +1,122 @@
+#
+# Copyright 2018 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.
+#
+from task import Task
+from enum import IntEnum
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, failure, TimeoutError
+from voltha.extensions.omci.omci_defs import ReasonCodes, EntityOperations
+from voltha.extensions.omci.omci_me import OntGFrame
+from voltha.extensions.omci.omci_cc import DEFAULT_OMCI_TIMEOUT
+
+RC = ReasonCodes
+OP = EntityOperations
+
+
+class RebootException(Exception):
+ pass
+
+
+class DeviceBusy(Exception):
+ pass
+
+
+class RebootFlags(IntEnum):
+ Reboot_Unconditionally = 0,
+ Reboot_If_No_POTS_VoIP_In_Progress = 1,
+ Reboot_If_No_Emergency_Call_In_Progress = 2
+
+
+class OmciRebootRequest(Task):
+ """
+ OpenOMCI routine to request reboot of an ONU
+ """
+ task_priority = Task.MAX_PRIORITY
+ name = "ONU OMCI Reboot Task"
+
+ def __init__(self, omci_agent, device_id,
+ flags=RebootFlags.Reboot_Unconditionally,
+ timeout=DEFAULT_OMCI_TIMEOUT):
+ """
+ Class initialization
+
+ :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+ :param flags: (RebootFlags) Reboot condition
+ """
+ super(OmciRebootRequest, self).__init__(OmciRebootRequest.name,
+ omci_agent,
+ device_id,
+ priority=OmciRebootRequest.task_priority,
+ exclusive=True)
+ self._device = omci_agent.get_device(device_id)
+ self._flags = flags
+ self._timeout = timeout
+ self._local_deferred = None
+
+ def cancel_deferred(self):
+ super(OmciRebootRequest, self).cancel_deferred()
+
+ d, self._local_deferred = self._local_deferred, None
+ try:
+ if d is not None and not d.called:
+ d.cancel()
+ except:
+ pass
+
+ def start(self):
+ """ Start task """
+ super(OmciRebootRequest, self).start()
+ self._local_deferred = reactor.callLater(0, self.perform_reboot)
+
+ @inlineCallbacks
+ def perform_reboot(self):
+ """
+ Perform the reboot requests
+
+ Depending on the ONU implementation, a response may not be returned. For this
+ reason, a timeout is considered successful.
+ """
+ self.log.info('perform-reboot')
+
+ try:
+ frame = OntGFrame().reboot(reboot_code=self._flags)
+ results = yield self._device.omci_cc.send(frame, timeout=self._timeout)
+
+ status = results.fields['omci_message'].fields['success_code']
+ self.log.debug('reboot-status', status=status)
+
+ # Did it fail
+ if status != RC.Success.value:
+ if self._flags != RebootFlags.Reboot_Unconditionally and\
+ status == RC.DeviceBusy.value:
+ raise DeviceBusy('ONU is busy, try again later')
+ else:
+ msg = 'Reboot request failed with status {}'.format(status)
+ raise RebootException(msg)
+
+ self.log.info('reboot-success')
+ self.deferred.callback(None)
+
+ except TimeoutError:
+ self.log.info('timeout', msg='Request timeout is not considered an error')
+ self.deferred.callback(None)
+
+ except DeviceBusy as e:
+ self.log.warn('perform-reboot', msg=e)
+ self.deferred.errback(failure.Failure(e))
+
+ except Exception as e:
+ self.log.exception('perform-reboot', e=e)
+ self.deferred.errback(failure.Failure(e))
diff --git a/voltha/extensions/omci/tasks/task.py b/voltha/extensions/omci/tasks/task.py
index 3bae4fc..9e10c65 100644
--- a/voltha/extensions/omci/tasks/task.py
+++ b/voltha/extensions/omci/tasks/task.py
@@ -33,6 +33,8 @@
Failure object.
"""
DEFAULT_PRIORITY = 128
+ MIN_PRIORITY = 0
+ MAX_PRIORITY = 255
_next_task_id = 0
def __init__(self, name, omci_agent, device_id, priority=DEFAULT_PRIORITY,
@@ -46,7 +48,8 @@
:param exclusive: (bool) If True, this task needs exclusive access to the
OMCI Communications channel when it runs
"""
- assert 0 <= priority <= 255, 'Priority should be 0..255'
+ assert Task.MIN_PRIORITY <= priority <= Task.MAX_PRIORITY, \
+ 'Priority should be {}..{}'.format(Task.MIN_PRIORITY, Task.MAX_PRIORITY)
Task._next_task_id += 1
self._task_id = Task._next_task_id