Maple OLT and Broadcom ONU adapter skeletons
Change-Id: I09729d17a3be221a64f3261a039dd9809a1ad65b
diff --git a/voltha/adapters/maple/__init__.py b/voltha/adapters/broadcom_onu/__init__.py
similarity index 100%
copy from voltha/adapters/maple/__init__.py
copy to voltha/adapters/broadcom_onu/__init__.py
diff --git a/voltha/adapters/broadcom_onu/broadcom_onu.py b/voltha/adapters/broadcom_onu/broadcom_onu.py
new file mode 100644
index 0000000..4ddeeb5
--- /dev/null
+++ b/voltha/adapters/broadcom_onu/broadcom_onu.py
@@ -0,0 +1,217 @@
+#
+# Copyright 2016 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.
+#
+
+"""
+Mock device adapter for testing.
+"""
+from uuid import uuid4
+
+import structlog
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, DeferredQueue
+from zope.interface import implementer
+
+from common.utils.asleep import asleep
+from voltha.adapters.interface import IAdapterInterface
+from voltha.core.logical_device_agent import mac_str_to_tuple
+from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
+from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Device, Port
+from voltha.protos.health_pb2 import HealthStatus
+from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
+ AdminState
+from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
+from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_1GB_FD, \
+ OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
+ OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
+
+log = structlog.get_logger()
+
+
+@implementer(IAdapterInterface)
+class BroadcomOnuAdapter(object):
+
+ name = 'broadcom_onu'
+
+ supported_device_types = [
+ DeviceType(
+ id='broadcom_onu',
+ adapter=name,
+ accepts_bulk_flow_update=True
+ )
+ ]
+
+ def __init__(self, adapter_agent, config):
+ self.adapter_agent = adapter_agent
+ self.config = config
+ self.descriptor = Adapter(
+ id=self.name,
+ vendor='Voltha project',
+ version='0.1',
+ config=AdapterConfig(log_level=LogLevel.INFO)
+ )
+ self.incoming_messages = DeferredQueue()
+
+ def start(self):
+ log.debug('starting')
+ log.info('started')
+
+ def stop(self):
+ log.debug('stopping')
+ log.info('stopped')
+
+ def adapter_descriptor(self):
+ return self.descriptor
+
+ def device_types(self):
+ return DeviceTypes(items=self.supported_device_types)
+
+ def health(self):
+ return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
+
+ def change_master_state(self, master):
+ raise NotImplementedError()
+
+ def adopt_device(self, device):
+ # We kick of a simulated activation scenario
+ reactor.callLater(0.2, self._simulate_device_activation, device)
+ return device
+
+ def abandon_device(self, device):
+ raise NotImplementedError()
+
+ def deactivate_device(self, device):
+ raise NotImplementedError()
+
+ @inlineCallbacks
+ def _simulate_device_activation(self, device):
+
+ # first we verify that we got parent reference and proxy info
+ assert device.parent_id
+ assert device.proxy_address.device_id
+ assert device.proxy_address.channel_id
+
+ # we pretend that we were able to contact the device and obtain
+ # additional information about it
+ device.vendor = 'Broadcom'
+ device.model = 'to be filled'
+ device.hardware_version = 'to be filled'
+ device.firmware_version = 'to be filled'
+ device.software_version = 'to be filled'
+ device.serial_number = uuid4().hex
+ device.connect_status = ConnectStatus.REACHABLE
+ self.adapter_agent.update_device(device)
+
+ # then shortly after we create some ports for the device
+ yield asleep(0.05)
+ uni_port = Port(
+ port_no=0,
+ label='UNI facing Ethernet port',
+ type=Port.ETHERNET_UNI,
+ admin_state=AdminState.ENABLED,
+ oper_status=OperStatus.ACTIVE
+ )
+ self.adapter_agent.add_port(device.id, uni_port)
+ self.adapter_agent.add_port(device.id, Port(
+ port_no=1,
+ label='PON port',
+ type=Port.PON_ONU,
+ admin_state=AdminState.ENABLED,
+ oper_status=OperStatus.ACTIVE,
+ peers=[
+ Port.PeerPort(
+ device_id=device.parent_id,
+ port_no=device.parent_port_no
+ )
+ ]
+ ))
+
+ # TODO adding vports to the logical device shall be done by agent?
+ # then we create the logical device port that corresponds to the UNI
+ # port of the device
+ yield asleep(0.05)
+
+ # obtain logical device id
+ parent_device = self.adapter_agent.get_device(device.parent_id)
+ logical_device_id = parent_device.parent_id
+ assert logical_device_id
+
+ # we are going to use the proxy_address.channel_id as unique number
+ # and name for the virtual ports, as this is guaranteed to be unique
+ # in the context of the OLT port, so it is also unique in the context
+ # of the logical device
+ port_no = device.proxy_address.channel_id
+ cap = OFPPF_1GB_FD | OFPPF_FIBER
+ self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
+ id=str(port_no),
+ ofp_port=ofp_port(
+ port_no=port_no,
+ hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % port_no),
+ name='uni-{}'.format(port_no),
+ config=0,
+ state=OFPPS_LIVE,
+ curr=cap,
+ advertised=cap,
+ peer=cap,
+ curr_speed=OFPPF_1GB_FD,
+ max_speed=OFPPF_1GB_FD
+ ),
+ device_id=device.id,
+ device_port_no=uni_port.port_no
+ ))
+
+ # simulate a proxied message sending and receving a reply
+ reply = yield self._simulate_message_exchange(device)
+
+ # and finally update to "ACTIVE"
+ device = self.adapter_agent.get_device(device.id)
+ device.oper_status = OperStatus.ACTIVE
+ self.adapter_agent.update_device(device)
+
+ def update_flows_bulk(self, device, flows, groups):
+ log.debug('bulk-flow-update', device_id=device.id,
+ flows=flows, groups=groups)
+
+ def update_flows_incrementally(self, device, flow_changes, group_changes):
+ raise NotImplementedError()
+
+ def send_proxied_message(self, proxy_address, msg):
+ raise NotImplementedError()
+
+ def receive_proxied_message(self, proxy_address, msg):
+ # just place incoming message to a list
+ self.incoming_messages.put((proxy_address, msg))
+
+ @inlineCallbacks
+ def _simulate_message_exchange(self, device):
+
+ # register for receiving async messages
+ self.adapter_agent.register_for_proxied_messages(device.proxy_address)
+
+ # reset incoming message queue
+ while self.incoming_messages.pending:
+ _ = yield self.incoming_messages.get()
+
+ # construct message
+ msg = 'test message'
+
+ # send message
+ self.adapter_agent.send_proxied_message(device.proxy_address, msg)
+
+ # wait till we detect incoming message
+ yield self.incoming_messages.get()
+
+ # by returning we allow the device to be shown as active, which
+ # indirectly verified that message passing works
diff --git a/voltha/adapters/maple/README.md b/voltha/adapters/maple/README.md
deleted file mode 100644
index e69de29..0000000
--- a/voltha/adapters/maple/README.md
+++ /dev/null
diff --git a/voltha/adapters/maple/maple.py b/voltha/adapters/maple/maple.py
deleted file mode 100644
index e69de29..0000000
--- a/voltha/adapters/maple/maple.py
+++ /dev/null
diff --git a/voltha/adapters/maple_olt/README.md b/voltha/adapters/maple_olt/README.md
new file mode 100644
index 0000000..c8b8599
--- /dev/null
+++ b/voltha/adapters/maple_olt/README.md
@@ -0,0 +1,134 @@
+# Developer notes:
+
+Before auto-discovery is implemented, you can follow the steps below to activate a Maple PON.
+These steps assume:
+
+* Voltha starts with fresh state (was just launched in single-instance mode)
+* Maple OLT and Broadcom ONU(s) are powered on and properly connected with splitters.
+* There is network reach from Voltha's host environment to the Broadcom Maple OLT via
+ a specific interface of the host OS. We symbolically refer to this Linux
+ interface as \<interface\>.
+* All commands are to be executed from the root dir of the voltha project after
+ env.sh was sourced, and at least 'make protos' was executed on the latest code.
+
+
+## Step 1: Launch Voltha with the proper interface value.
+
+```
+./voltha/main.py -I <interface> # example: ./voltha/main.py -I eth2
+```
+
+## Step 2: Launch Chameleon (in a different terminal)
+
+```
+./chamaleon/main.py
+```
+
+## Step 3: Verify Maple and Broadcom adapters loaded
+
+In a third terminal, issue the following REST requests:
+
+```
+curl -s http://localhost:8881/api/v1/local/adapters | jq
+```
+
+This should list (among other entries) two entries for Broadcom devices:
+one for the Maple OLT and one for the Broadcom ONU.
+
+The following request should show the device types supported:
+
+```
+curl -s http://localhost:8881/api/v1/local/device_types | jq
+```
+
+This should include two entries for Broadcom devices, one for the OLT
+and one for the ONU.
+
+## Step 4: Pre-provision a Maple OLT
+
+Issue the following command to pre-provision the Maple OLT:
+
+```
+curl -s -X POST -d '{"type": "maple_olt", "mac_address": "00:00:00:00:00:01"}' \
+ http://localhost:8881/api/v1/local/devices | jq '.' | tee olt.json
+```
+
+This shall return with a complete Device JSON object, including a 12-character
+id of the new device and a preprovisioned state as admin state (it also saved the
+json blob in a olt.json file):
+
+```
+{
+ "vendor": "",
+ "software_version": "",
+ "parent_port_no": 0,
+ "connect_status": "UNKNOWN",
+ "root": false,
+ "adapter": "tibit_olt",
+ "vlan": 0,
+ "hardware_version": "",
+ "ports": [],
+ "parent_id": "",
+ "oper_status": "UNKNOWN",
+ "admin_state": "PREPROVISIONED",
+ "mac_address": "00:00:00:00:00:01",
+ "serial_number": "",
+ "model": "",
+ "type": "maple_olt",
+ "id": "2db8e16804ec",
+ "firmware_version": ""
+}
+```
+
+For simplicity, store the device id as shell variable:
+
+```
+OLT_ID=$(jq .id olt.json | sed 's/"//g')
+```
+
+## Step 5: Activate the OLT
+
+To activate the OLT, issue the following using the OLT_ID memorized above:
+
+```
+curl -s -X POST http://localhost:8881/api/v1/local/devices/$OLT_ID/activate
+```
+
+After this, if you retrieve the state of the OLT device, it should be enabled
+and in the 'ACTIVATING' operational status:
+
+```
+curl http://localhost:8881/api/v1/local/devices/$OLT_ID | jq '.oper_status,.admin_state'
+"ACTIVATING"
+"ENABLED"
+```
+When the device is ACTIVE, the logical devices and logical ports should be created. To check
+the logical devices and logical ports, use the following commands.
+
+```
+curl -s http://localhost:8881/api/v1/local/logical_devices | jq '.'
+# Note: Need to pull out logical device id.
+curl -s http://localhost:8881/api/v1/local/logical_devices/47d2bb42a2c6/ports | jq '.'
+```
+
+
+
+
+
+# OLD stuff
+
+[This will be moved to some other place soon.]
+
+To get the EOAM stack to work with the ONOS olt-test, the following
+command was used in the shell to launch the olt-test.
+
+NOTE: This command should soon be eliminated as the adapter should
+be started by VOLTHA. By running the commands as listed below, then
+the olt-test can take advantage of the virtual environment.
+
+```
+$ cd <LOCATION_OF_VOLTHA>
+$ sudo -s
+# . ./env.sh
+(venv-linux) # PYTHONPATH=$HOME/dev/voltha/voltha/adapters/tibit ./oftest/oft --test-dir=olt-oftest/ -i 1@enp1s0f0 -i 2@enp1s0f1 --port 6633 -V 1.3 -t "olt_port=1;onu_port=2;in_out_port=1;device_type='tibit'" olt-complex.TestScenario1SingleOnu
+```
diff --git a/voltha/adapters/maple/__init__.py b/voltha/adapters/maple_olt/__init__.py
similarity index 100%
rename from voltha/adapters/maple/__init__.py
rename to voltha/adapters/maple_olt/__init__.py
diff --git a/voltha/adapters/maple_olt/maple_olt.py b/voltha/adapters/maple_olt/maple_olt.py
new file mode 100644
index 0000000..f250ae9
--- /dev/null
+++ b/voltha/adapters/maple_olt/maple_olt.py
@@ -0,0 +1,222 @@
+#
+# Copyright 2016 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 uuid import uuid4
+
+import structlog
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks
+from zope.interface import implementer
+
+from common.utils.asleep import asleep
+from voltha.adapters.interface import IAdapterInterface
+from voltha.core.logical_device_agent import mac_str_to_tuple
+from voltha.protos.adapter_pb2 import Adapter, AdapterConfig
+from voltha.protos.device_pb2 import DeviceType, DeviceTypes, Device, Port
+from voltha.protos.health_pb2 import HealthStatus
+from voltha.protos.common_pb2 import LogLevel, OperStatus, ConnectStatus, \
+ AdminState
+from voltha.protos.logical_device_pb2 import LogicalDevice, LogicalPort
+from voltha.protos.openflow_13_pb2 import ofp_desc, ofp_port, OFPPF_1GB_FD, \
+ OFPPF_FIBER, OFPPS_LIVE, ofp_switch_features, OFPC_PORT_STATS, \
+ OFPC_GROUP_STATS, OFPC_TABLE_STATS, OFPC_FLOW_STATS
+
+log = structlog.get_logger()
+
+
+@implementer(IAdapterInterface)
+class MapleOltAdapter(object):
+
+ name = 'maple_olt'
+
+ supported_device_types = [
+ DeviceType(
+ id='maple_olt',
+ adapter=name,
+ accepts_bulk_flow_update=True
+ )
+ ]
+
+ def __init__(self, adapter_agent, config):
+ self.adapter_agent = adapter_agent
+ self.config = config
+ self.descriptor = Adapter(
+ id=self.name,
+ vendor='Voltha project',
+ version='0.1',
+ config=AdapterConfig(log_level=LogLevel.INFO)
+ )
+
+ def start(self):
+ log.debug('starting')
+ log.info('started')
+
+ def stop(self):
+ log.debug('stopping')
+ log.info('stopped')
+
+ def adapter_descriptor(self):
+ return self.descriptor
+
+ def device_types(self):
+ return DeviceTypes(items=self.supported_device_types)
+
+ def health(self):
+ return HealthStatus(state=HealthStatus.HealthState.HEALTHY)
+
+ def change_master_state(self, master):
+ raise NotImplementedError()
+
+ def adopt_device(self, device):
+ log.info('adopt-device', device=device)
+ # We kick of a simulated activation scenario
+ reactor.callLater(0.2, self._activate_device, device)
+ return device
+
+ def abandon_device(self, device):
+ raise NotImplementedError(0
+ )
+ def deactivate_device(self, device):
+ raise NotImplementedError()
+
+ def _activate_device(self, device):
+
+ # first we pretend that we were able to contact the device and obtain
+ # additional information about it
+ device.root = True
+ device.vendor = 'Broadcom'
+ device.model = 'Maple XYZ'
+ device.hardware_version = 'Fill this'
+ device.firmware_version = 'Fill this'
+ device.software_version = 'Fill this'
+ device.serial_number = 'Fill this'
+ device.connect_status = ConnectStatus.REACHABLE
+ self.adapter_agent.update_device(device)
+
+ # register ports
+ nni_port = Port(
+ port_no=0,
+ label='NNI facing Ethernet port',
+ type=Port.ETHERNET_NNI,
+ admin_state=AdminState.ENABLED,
+ oper_status=OperStatus.ACTIVE
+ )
+ self.adapter_agent.add_port(device.id, nni_port)
+ self.adapter_agent.add_port(device.id, Port(
+ port_no=1,
+ label='PON port',
+ type=Port.PON_OLT,
+ admin_state=AdminState.ENABLED,
+ oper_status=OperStatus.ACTIVE
+ ))
+
+ # register logical device (as we are a root device)
+ logical_device_id = uuid4().hex[:12]
+ ld = LogicalDevice(
+ id=logical_device_id,
+ datapath_id=int('0x' + logical_device_id[:8], 16), # from id
+ desc=ofp_desc(
+ mfr_desc='cord porject',
+ hw_desc='n/a',
+ sw_desc='logical device for Maple-based PON',
+ serial_num=uuid4().hex,
+ dp_desc='n/a'
+ ),
+ switch_features=ofp_switch_features(
+ n_buffers=256, # TODO fake for now
+ n_tables=2, # TODO ditto
+ capabilities=( # TODO and ditto
+ OFPC_FLOW_STATS
+ | OFPC_TABLE_STATS
+ | OFPC_PORT_STATS
+ | OFPC_GROUP_STATS
+ )
+ ),
+ root_device_id=device.id
+ )
+ self.adapter_agent.create_logical_device(ld)
+ cap = OFPPF_1GB_FD | OFPPF_FIBER
+ self.adapter_agent.add_logical_port(ld.id, LogicalPort(
+ id='nni',
+ ofp_port=ofp_port(
+ port_no=0,
+ hw_addr=mac_str_to_tuple('00:00:00:00:00:%02x' % 129),
+ name='nni',
+ config=0,
+ state=OFPPS_LIVE,
+ curr=cap,
+ advertised=cap,
+ peer=cap,
+ curr_speed=OFPPF_1GB_FD,
+ max_speed=OFPPF_1GB_FD
+ ),
+ device_id=device.id,
+ device_port_no=nni_port.port_no,
+ root_port=True
+ ))
+
+ # and finally update to active
+ device = self.adapter_agent.get_device(device.id)
+ device.parent_id = ld.id
+ device.oper_status = OperStatus.ACTIVE
+ self.adapter_agent.update_device(device)
+
+ reactor.callLater(0.1, self._simulate_detection_of_onus, device)
+
+ def _simulate_detection_of_onus(self, device):
+ for i in xrange(1, 5):
+ log.info('activate-olt-for-onu-{}'.format(i))
+ vlan_id = self._olt_side_onu_activation(i)
+ self.adapter_agent.child_device_detected(
+ parent_device_id=device.id,
+ parent_port_no=1,
+ child_device_type='broadcom_onu',
+ proxy_address=Device.ProxyAddress(
+ device_id=device.id,
+ channel_id=vlan_id
+ ),
+ vlan=vlan_id
+ )
+
+ def _olt_side_onu_activation(self, seq):
+ """
+ This is where if this was a real OLT, the OLT-side activation for
+ the new ONU should be performed. By the time we return, the OLT shall
+ be able to provide tunneled (proxy) communication to the given ONU,
+ using the returned information.
+ """
+ vlan_id = seq + 100
+ return vlan_id
+
+ def update_flows_bulk(self, device, flows, groups):
+ log.debug('bulk-flow-update', device_id=device.id,
+ flows=flows, groups=groups)
+
+ def update_flows_incrementally(self, device, flow_changes, group_changes):
+ raise NotImplementedError()
+
+ def send_proxied_message(self, proxy_address, msg):
+ log.info('send-proxied-message', proxy_address=proxy_address, msg=msg)
+ # we mimic a response by sending the same message back in a short time
+ reactor.callLater(
+ 0.2,
+ self.adapter_agent.receive_proxied_message,
+ proxy_address,
+ msg
+ )
+
+ def receive_proxied_message(self, proxy_address, msg):
+ raise NotImplementedError()