VOL-1330: Fix bug in table based ME and Ready only attribute

Tables must be sent a row at a time
to fit in an omci payload. Also each
row needs the same key set since omci
table ops are additive.

Also fix bug where PPTP Ethernet entity
type has operational status set as writable,
which it is not per the spec.  This being
falsely writable caused reconciliation to attempt
to set oper status along with admin state,
causing the onu to reject the entire message.

Both of these caused reconciliation to never finish.

Change-Id: Ib2fc3fd8bf05910ff06a9f1d44e3adeca8dae0c2
diff --git a/VERSION b/VERSION
index 7b3b241..7d2ed7c 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.1.4-dev0
+2.1.4
diff --git a/pyvoltha/adapters/extensions/omci/omci_entities.py b/pyvoltha/adapters/extensions/omci/omci_entities.py
index 1db5d8a..95bad37 100644
--- a/pyvoltha/adapters/extensions/omci/omci_entities.py
+++ b/pyvoltha/adapters/extensions/omci/omci_entities.py
@@ -346,7 +346,7 @@
             range_check=lambda x: x in [0, 3]),
         ECA(ByteField("administrative_state", 1), {AA.R, AA.W},
             range_check=lambda x: 0 <= x <= 1),
-        ECA(ByteField("operational_state", 1), {AA.R, AA.W},
+        ECA(ByteField("operational_state", 1), {AA.R},
             range_check=lambda x: 0 <= x <= 1, optional=True, avc=True),
         ECA(ByteField("config_ind", 0), {AA.R},
             range_check=lambda x: x in [0, 1, 2, 3, 4, 0x11, 0x12, 0x13]),
diff --git a/pyvoltha/adapters/extensions/omci/tasks/mib_reconcile_task.py b/pyvoltha/adapters/extensions/omci/tasks/mib_reconcile_task.py
index 421879b..b2eedd2 100644
--- a/pyvoltha/adapters/extensions/omci/tasks/mib_reconcile_task.py
+++ b/pyvoltha/adapters/extensions/omci/tasks/mib_reconcile_task.py
@@ -21,6 +21,7 @@
 from pyvoltha.adapters.extensions.omci.omci_defs import *
 from pyvoltha.adapters.extensions.omci.omci_me import OntDataFrame
 from pyvoltha.adapters.extensions.omci.omci_frame import OmciFrame, OmciDelete, OmciCreate, OmciSet
+from pyvoltha.adapters.extensions.omci.omci_fields import OmciTableField
 from pyvoltha.adapters.extensions.omci.database.mib_db_api import ATTRIBUTES_KEY
 
 OP = EntityOperations
@@ -420,13 +421,18 @@
                     self._db_updates = 0
 
                     # Try any writeable attributes now (but not set-by-create)
-                    writeable_data = {k: v for k, v in olt_entry[ATTRIBUTES_KEY].items()
-                                      if AA.Writable in
-                                      next((attr.access for attr in me_entry.attributes
-                                            if attr.field.name == k), set())
-                                      and AA.SetByCreate not in
-                                      next((attr.access for attr in me_entry.attributes
-                                            if attr.field.name == k), set())}
+                    writeable_data = dict()
+                    table_data = dict()
+                    for key, value in olt_entry[ATTRIBUTES_KEY].items():
+                        for attr in me_entry.attributes:
+                            if AA.SetByCreate in attr.access:
+                                continue
+                            if AA.Writable in attr.access:
+                                if attr.field.name == key:
+                                    if isinstance(attr.field, OmciTableField):
+                                        table_data[key] = value
+                                    else:
+                                        writeable_data[key] = value
 
                     if len(writeable_data):
                         attributes_mask = me_entry.mask_for(*writeable_data.keys())
@@ -436,11 +442,24 @@
                                                                entity_id=eid,
                                                                attributes_mask=attributes_mask,
                                                                data=writeable_data))
-
                         self._local_deferred = yield self._device.omci_cc.send(frame)
                         self.check_status_and_state(self._local_deferred, 'olt-set-writeable')
                         successes += 1
 
+                    for key, value in table_data.items():
+                        for row in value:
+                            setvalue = { key : row }
+                            attributes_mask = me_entry.mask_for(*setvalue.keys())
+                            frame = OmciFrame(transaction_id=None,
+                                              message_type=OmciSet.message_id,
+                                              omci_message=OmciSet(entity_class=cid,
+                                                                   entity_id=eid,
+                                                                   attributes_mask=attributes_mask,
+                                                                   data=setvalue))
+                            self._local_deferred = yield self._device.omci_cc.send(frame)
+                            self.check_status_and_state(self._local_deferred, 'olt-set-table')
+                            successes += 1
+
             except Exception as e:
                 self.log.exception('olt-only-fix', e=e, cid=cid, eid=eid)
                 failures += 1