VOL-670: Implement OF port_mod handling for logical devices.

Change-Id: I882790f7d70ec812ba6adc0d01e128ed6a98cbbf
diff --git a/ofagent/agent.py b/ofagent/agent.py
index cb59d24..6117c8f 100644
--- a/ofagent/agent.py
+++ b/ofagent/agent.py
@@ -18,17 +18,13 @@
 
 import structlog
 import os.path
-from twisted.internet import protocol
-from twisted.internet import reactor
-from twisted.internet import reactor, ssl
-from twisted.internet import reactor
+from twisted.internet import protocol, reactor, ssl
 from twisted.internet.defer import Deferred, inlineCallbacks
 
 import loxi.of13 as of13
 from common.utils.asleep import asleep
 from of_connection import OpenFlowConnection
 from of_protocol_handler import OpenFlowProtocolHandler
-# from ofagent.protos.openflow_13_pb2 import ChangeEvent
 
 log = structlog.get_logger()
 
@@ -208,4 +204,3 @@
 
     reactor.addSystemEventTrigger('before', 'shutdown', shutdown)
     reactor.run()
-
diff --git a/ofagent/grpc_client.py b/ofagent/grpc_client.py
index ccdb98b..1214507 100644
--- a/ofagent/grpc_client.py
+++ b/ofagent/grpc_client.py
@@ -28,7 +28,7 @@
 from twisted.internet.defer import inlineCallbacks, returnValue, DeferredQueue
 
 from protos.voltha_pb2 import ID, VolthaLocalServiceStub, FlowTableUpdate, \
-    FlowGroupTableUpdate, PacketOut
+    FlowGroupTableUpdate, PacketOut, LogicalPortId
 from google.protobuf import empty_pb2
 
 
@@ -148,6 +148,13 @@
         self.packet_out_queue.put(packet_out)
 
     @inlineCallbacks
+    def get_port(self, device_id, port_id):
+        req = LogicalPortId(id=device_id, port_id=port_id)
+        res = yield threads.deferToThread(
+            self.local_stub.GetLogicalDevicePort, req)
+        returnValue(res)
+
+    @inlineCallbacks
     def get_port_list(self, device_id):
         req = ID(id=device_id)
         res = yield threads.deferToThread(
@@ -155,6 +162,26 @@
         returnValue(res.items)
 
     @inlineCallbacks
+    def enable_port(self, device_id, port_id):
+        req = LogicalPortId(
+            id=device_id,
+            port_id=port_id
+        )
+        res = yield threads.deferToThread(
+            self.local_stub.EnableLogicalDevicePort, req)
+        returnValue(res)
+
+    @inlineCallbacks
+    def disable_port(self, device_id, port_id):
+        req = LogicalPortId(
+            id=device_id,
+            port_id=port_id
+        )
+        res = yield threads.deferToThread(
+            self.local_stub.DisableLogicalDevicePort, req)
+        returnValue(res)
+
+    @inlineCallbacks
     def get_device_info(self, device_id):
         req = ID(id=device_id)
         res = yield threads.deferToThread(
diff --git a/ofagent/of_protocol_handler.py b/ofagent/of_protocol_handler.py
index 1780244..d6aa92d 100644
--- a/ofagent/of_protocol_handler.py
+++ b/ofagent/of_protocol_handler.py
@@ -178,8 +178,20 @@
         # https://jira.opencord.org/browse/CORD-826
         pass
 
+    @inlineCallbacks
     def handle_port_mod_request(self, req):
-        raise NotImplementedError()
+        if self.role == ofp.OFPCR_ROLE_MASTER or self.role == ofp.OFPCR_ROLE_EQUAL:
+            port = yield self.rpc.get_port(self.device_id, str(req.port_no))
+
+            if port.ofp_port.config & ofp.OFPPC_PORT_DOWN != \
+                    req.config & ofp.OFPPC_PORT_DOWN:
+                if req.config & ofp.OFPPC_PORT_DOWN:
+                    self.rpc.disable_port(self.device_id, port.id)
+                else:
+                    self.rpc.enable_port(self.device_id, port.id)
+
+        elif self.role == ofp.OFPCR_ROLE_SLAVE:
+            self.cxn.send(ofp.message.bad_request_error_msg(code=ofp.OFPBRC_IS_SLAVE))
 
     def handle_table_mod_request(self, req):
         raise NotImplementedError()