VOL-2583 insert multicast entities frame

Change-Id: Ieed98cd83d0bace78a81252c105eea2695d7b1a5
diff --git a/VERSION b/VERSION
index be14458..ed09221 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-2.3.15
+2.3.16
diff --git a/pyvoltha/adapters/extensions/omci/omci_me.py b/pyvoltha/adapters/extensions/omci/omci_me.py
index 57412a1..69a157c 100644
--- a/pyvoltha/adapters/extensions/omci/omci_me.py
+++ b/pyvoltha/adapters/extensions/omci/omci_me.py
@@ -967,3 +967,332 @@
             data['weight'] = weight
 
         super(PriorityQueueFrame, self).__init__(PriorityQueueG, entity_id, data)
+
+class MulticastGemInterworkingTPFrame(MEFrame):
+    def __init__(self, entity_id, gem_port_network_ctp_pointer=None, interworking_option=None,
+                 service_profile_pointer=None, pptp_counter=None, gal_profile_pointer=None,
+                 ipv4_multicast_address_table=None, attributes=None):
+        """
+        :param entity_id: (int) This attribute uniquely identifies each instance of
+                                this managed entity. (0..65535)
+
+        :param gemportctp_pointer_id: (int) This attribute points to an instance of
+                                the GEM port network CTP (0...65535)
+
+        :param interworking_option: (int) This attribute identifies the type
+                        of non-GEM function that is being interworked.
+                        The options are:
+                            0 Circuit-emulated TDM
+                            1 MAC bridged LAN
+                            2 Reserved
+                            3 Reserved
+                            4 Video return path
+                            5 IEEE 802.1p mapper
+                            6 Downstream broadcast
+                            7 MPLS PW TDM service
+
+        :param service_profile_pointer: (int) This attribute points to an instance of
+                                      a service profile.
+                    CES service profile                 if interworking option = 0
+                    MAC bridge service profile          if interworking option = 1
+                    Video return path service profile   if interworking option = 4
+                    IEEE 802.1p mapper service profile  if interworking option = 5
+                    Null pointer                        if interworking option = 6
+                    CES service profile                 if interworking option = 7
+
+        :param pptp_counter:
+
+        :param gal_profile_pointer: (int) This attribute points to an instance of
+                                      a service profile.
+
+        :param ipv4_multicast_address_table: (dict) This attribute maps IP multicast addresses
+                                            to PON layer addresses.
+                    GEM port-ID                                              2 bytes
+                    Secondary key                                            2 bytes
+                    IP multicast destination address range start             4 bytes
+                    IP multicast destination address range stop              4 bytes
+
+        :param attributes: (basestring, list, set, dict) additional ME attributes.
+                   not specifically specified as a parameter. For gets
+                   a string, list, or set can be provided. For create/set
+                   operations, a dictionary should be provided, for
+                   deletes None may be specified..
+        """
+
+        # Validate
+        self.check_type(gem_port_network_ctp_pointer, (int, type(None)))
+        self.check_type(interworking_option, (int, type(None)))
+        self.check_type(service_profile_pointer, (int, type(None)))
+        self.check_type(pptp_counter, (int, type(None)))
+        self.check_type(gal_profile_pointer, (int, type(None)))
+
+        if gem_port_network_ctp_pointer is not None and not 0 <= gem_port_network_ctp_pointer <= 0xFFFE:  # TODO: Verify max
+            raise ValueError('gem_port_network_ctp_pointer should be 0..0xFFFE')
+
+        if interworking_option is not None and not 0 <= interworking_option <= 7:
+            raise ValueError('interworking_option should be 0..7')
+
+        if service_profile_pointer is not None and not 0 <= service_profile_pointer <= 0xFFFE:  # TODO: Verify max
+            raise ValueError('service_profile_pointer should be 0..0xFFFE')
+
+        if pptp_counter is not None and not 0 <= pptp_counter <= 255:  # TODO: Verify max
+            raise ValueError('pptp_counter should be 0..255')
+
+        if gal_profile_pointer is not None and not 0 <= gal_profile_pointer <= 0xFFFE:  # TODO: Verify max
+            raise ValueError('gal_profile_pointer should be 0..0xFFFE')
+
+
+        data = MEFrame._attr_to_data(attributes)
+
+        if gem_port_network_ctp_pointer is not None or \
+                interworking_option is not None or \
+                service_profile_pointer is not None or \
+                pptp_counter is not None or \
+                gal_profile_pointer is not None or \
+                ipv4_multicast_address_table is not None:
+
+            data = data or dict()
+
+            if gem_port_network_ctp_pointer is not None:
+                data['gem_port_network_ctp_pointer'] = gem_port_network_ctp_pointer
+            if interworking_option is not None:
+                data['interworking_option'] = interworking_option
+            if service_profile_pointer is not None:
+                data['service_profile_pointer'] = service_profile_pointer
+            if pptp_counter is not None:
+                data['pptp_counter'] = pptp_counter
+            if gal_profile_pointer is not None:
+                data['gal_profile_pointer'] = gal_profile_pointer
+            if ipv4_multicast_address_table is not None:
+                data['ipv4_multicast_address_table'] = ipv4_multicast_address_table
+
+        super(MulticastGemInterworkingTPFrame, self).__init__(MulticastGemInterworkingTp,
+                                                              entity_id,
+                                                              data)
+
+
+class MulticastSubscriberConfigInfoFrame (MEFrame):
+    def __init__(self, entity_id, me_type=None, multicast_operations_profile_pointer=None,
+                 max_simultaneous_groups=None, max_multicast_bandwidth=None, bandwidth_enforcement=None,
+                 multicast_service_package_table=None, allowed_preview_groups_table=None, attributes=None):
+        """
+
+        :param entity_id:  (int) This attribute uniquely identifies each instance of
+                                this managed entity. (0..65535)
+        :param me_type: (int) This attribute indicates the type of the ME implicitly linked by the managed
+                              entity ID attribute
+
+                            0  MAC bridge port config data
+                            1 IEEE 802.1p mapper service profile
+
+        :param multicast_operations_profile_pointer: (int) This attribute points to an instance of the
+                                                     multicast operations profile.
+
+        :param max_simultaneous_groups: This attribute specifies the maximum number of dynamic multicast
+                                        groups that may be replicated to the client port at any one time.
+
+        :param max_multicast_bandwidth:  This attribute specifies the maximum imputed dynamic bandwidth,
+                                        in bytes per second, that may be delivered to the client port at
+                                        any one time.
+
+        :param bandwidth_enforcement: The recommended default value of this boolean attribute is false,
+                                               and specifies that attempts to exceed the max multicast
+                                               bandwidth be counted but honoured.
+
+        :param multicast_service_package_table: This attribute is a list that specifies one or more multicast
+                                                service packages.
+
+        :param allowed_preview_groups_table: This attribute is a list that specifies the preview groups that
+                                             are currently allowed for the UNI associated with this ME.
+        """
+
+        self.check_type(me_type, (int, type(None)))
+        self.check_type(multicast_operations_profile_pointer, (int, type(None)))
+        self.check_type(max_simultaneous_groups, (int, type(None)))
+        self.check_type(max_multicast_bandwidth, (int, type(None)))
+        self.check_type(bandwidth_enforcement, (bool, type(None)))
+        self.check_type(multicast_service_package_table, (int, type(None)))
+        self.check_type(allowed_preview_groups_table, (int, type(None)))
+
+        if me_type is not None and not 0 <= me_type <= 1:
+            raise ValueError(' me_type should be 0 or 1')
+        if multicast_operations_profile_pointer is not None and not 0 <= multicast_operations_profile_pointer <= 0xFFFE:
+            raise ValueError(' multicast_operations_profile_pointer should be 0 ... 0xFFFE')
+        if max_simultaneous_groups is not None and not 0 <= max_simultaneous_groups <= 0xFFFE:
+            raise ValueError('max_simultaneous_groups should be 0 ... 0xFFFE')
+        if max_multicast_bandwidth is not None and not 0 <= max_multicast_bandwidth <= 0xFFFE:
+            raise ValueError('max_multicast_bandwidth should be 0 ... 0xFFFE')
+        if bandwidth_enforcement is not None and not bandwidth_enforcement != False and not bandwidth_enforcement != True:
+            raise ValueError('bandwidth_enforcement should be true or false')
+        if allowed_preview_groups_table is not None and not 0 <= allowed_preview_groups_table <= 0xFFFE:
+            raise ValueError('allowed_preview_groups_table should be 0 ... 0xFFFE')
+
+        data = MEFrame._attr_to_data ( attributes )
+
+        if me_type is not None or \
+                multicast_operations_profile_pointer is not None or \
+                max_simultaneous_groups is not None or \
+                max_multicast_bandwidth is not None or \
+                bandwidth_enforcement is not None or \
+                allowed_preview_groups_table is not None:
+
+            data = data or dict ()
+
+            if me_type is not None:
+                data['me_type'] = me_type
+            if multicast_operations_profile_pointer is not None:
+                data['multicast_operations_profile_pointer'] = multicast_operations_profile_pointer
+            if max_simultaneous_groups is not None:
+                data['max_simultaneous_groups'] = max_simultaneous_groups
+            if bandwidth_enforcement is not None:
+                data['bandwidth_enforcement'] = bandwidth_enforcement
+            if allowed_preview_groups_table is not None:
+                data['allowed_preview_groups_table'] = allowed_preview_groups_table
+
+        super(MulticastSubscriberConfigInfoFrame, self).__init__(MulticastSubscriberConfigInfo,
+                                                                       entity_id,
+                                                                       data)
+
+
+class MulticastOperationsProfileFrame(MEFrame):
+    def __init__(self, entity_id, igmp_version=None, igmp_function=None,
+                 immediate_leave=None, upstream_igmp_tci=None,
+                 upstream_igmp_tag_control=None, upstream_igmp_rate=None,
+                 dynamic_access_control_list_table=None, static_access_control_list_table=None,
+                 lost_groups_list_table=None, robustness=None, querier_ip_address=None,
+                 query_interval=None, query_max_response=None, last_member_query_interval=None,
+                 unauthorized_join_request_behavior=None, downstream_igmp_and_multicast_tci=None,
+                 attributes=None):
+
+        """
+
+        :param entity_id: (int) This attribute uniquely identifies each instance of
+                                this managed entity. (0..65535)
+
+        :param igmp_version: This attribute specifies the version of IGMP to be supported.
+                             1  IGMP version 1
+                             2  IGMP version 2
+                             3  IGMP version 3
+                             16 MLD version 1
+                             17 MLD version 2
+
+        :param igmp_function: This attribute enables an IGMP function.
+                             0  Only IGMP snooping
+                             1  Snooping with proxy reporting
+                             2  IGMP proxy
+
+        :param immediate_leave: This boolean attribute controls the immediate leave function
+                             False    Disable Immediate Leave
+                             True     Enable Immediate Leave
+
+        :param upstream_igmp_tci: Under control of the upstream IGMP tag control attribute,
+                                  the upstream IGMP TCI attribute defines a VLAN ID and
+                                  P-bits to add upstream IGMP messages.
+
+        :param upstream_igmp_tag_control: This attribute controls the upstream IGMP TCI attribute.
+                             0   Pass upstream IGMP traffic transparently
+                             1   Add a VLAN tag to upstream IGMP traffic
+                             2   Replace the entire TCI on upstream IGMP traffic
+                             3   Replace only the VLAN ID on upstream IGMP traffic,
+                                 retaining the orginal DEI and P bits.
+
+        :param upstream_igmp_rate: This attribute limits the maximum rate of upstream IGMP traffic.
+
+        :param dynamic_access_control_list_table: This attribute is a list that specifies one or
+                                                  more multicast group address ranges.
+
+        :param static_access_control_list_table: This attribute is a list that specifies one or
+                                                 more multicast group address ranges.
+
+        :param lost_groups_list_table: This attributes is a list of groups from the dynamic access
+                                       control list table for which there is an active join, but no
+                                       downstream flow is present, possibly because of source failure,
+                                       but also possibly because of misconfiguration somewhere upstream.
+
+        :param robustness: This attributes allows tuning for possible packet loss in the network.
+
+        :param querier_ip_address: This attribute specifies the IP address to be used by a proxy querier.
+
+        :param query_interval: This attribute specifies the interval between general queries in seconds.
+
+        :param query_max_response: This attribute is the max response time added by the proxy into general
+                                   query messages directed to UNIs.
+
+        :param last_member_query_interval: This attribute specifies the maximum response time inserted into
+                                           group-specific queries sent to UNIs in response to group leave messages.
+
+        :param unauthorized_join_request_behavior: This boolean attribute specifies the ONU's behaviour when it
+                                                   receives an IGMP join request for a group that is not authorized
+                                                   in the dynamic address control list table, or an IGMPv3 membership
+                                                   report for groups, none of which are authorized in the dynamic ACL.
+
+        :param downstream_igmp_and_multicast_tci: This attribute controls the downstream tagging of both the
+                                                  IGMP and multicast frames.
+
+                               0    Pass the downstream IGMP and multicast traffic transparently.
+                               1    Strip the outer VLAN tag from the downstream IGMP and multicast traffic
+                               2    Add a tag on to the downstream IGMP and multicast traffic.
+                               3    Replace the tag on the downstream IGMP and multicast traffic.
+                               4    Replace only the VLAN ID on the downstream IGMP and multicast traffic
+                               5    Add a tag on to the downstream IGMP and multicast traffic.
+                               6    Replace the tag on the downstream IGMP and multicast traffic
+                               7    Replace only the VID on the downstream IGMP and multicast traffic.
+
+        """
+        data = MEFrame._attr_to_data(attributes)
+
+        if igmp_version is not None or \
+                igmp_function is not None or \
+                immediate_leave is not None or \
+                upstream_igmp_tci is not None or \
+                upstream_igmp_tag_control is not None or \
+                upstream_igmp_rate is not None or \
+                dynamic_access_control_list_table is not None or \
+                static_access_control_list_table is not None or \
+                lost_groups_list_table is not None or \
+                robustness is not None or \
+                querier_ip_address is not None or \
+                query_interval is not None or \
+                query_max_response is not None or \
+                last_member_query_interval is not None or \
+                unauthorized_join_request_behavior is not None or \
+                downstream_igmp_and_multicast_tci is not None:
+
+            data = data or dict()
+
+            if igmp_version is not None:
+                data['igmp_version'] = igmp_version
+            if igmp_function is not None:
+                data['igmp_function'] = igmp_function
+            if immediate_leave is not None:
+                data['immediate_leave'] = immediate_leave
+            if upstream_igmp_tci is not None:
+                data['us_igmp_tci'] = upstream_igmp_tci
+            if upstream_igmp_tag_control is not None:
+                data['us_igmp_tag_ctrl'] = upstream_igmp_tag_control
+            if upstream_igmp_rate is not None:
+                data['us_igmp_rate'] = upstream_igmp_rate
+            if dynamic_access_control_list_table is not None:
+                data['dynamic_access_control_list_table'] = dynamic_access_control_list_table
+            if static_access_control_list_table is not None:
+                data['static_access_control_list_table'] = static_access_control_list_table
+            if lost_groups_list_table is not None:
+                data['lost_groups_list_table'] = lost_groups_list_table
+            if robustness is not None:
+                data['robustness'] = robustness
+            if querier_ip_address is not None:
+                data['querier_ip'] = querier_ip_address
+            if query_interval is not None:
+                data['query_interval'] = query_interval
+            if query_max_response is not None:
+                data['querier_max_response_time'] = query_max_response
+            if last_member_query_interval is not None:
+                data['last_member_response_time'] = last_member_query_interval
+            if unauthorized_join_request_behavior is not None:
+                data['unauthorized_join_behaviour'] = unauthorized_join_request_behavior
+            if downstream_igmp_and_multicast_tci is not None:
+                data['ds_igmp_mcast_tci'] = downstream_igmp_and_multicast_tci
+
+        super(MulticastOperationsProfileFrame, self).__init__(MulticastOperationsProfile,
+                                                              entity_id,
+                                                              data)