# Copyright (c) 2023 Baidu.com, Inc. All Rights Reserved
#
# 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.

"""
This module provides a client class for Et Channel IPv6.
"""

import copy
import json
import uuid

from baidubce import bce_base_client
from baidubce import compat
from baidubce import utils
from baidubce.auth import bce_v1_signer
from baidubce.http import bce_http_client
from baidubce.http import handler
from baidubce.http import http_methods
from baidubce.utils import required
from baidubce.services.et.model import ETStatus

class EtClient(bce_base_client.BceBaseClient):
    """
    Et base sdk client
    """
    prefix = b'/v1'
    et = 'et'
    init = 'init'
    channel = 'channel'
    route = 'route'
    rule = 'rule'

    def __init__(self, config=None):
        """
        :type config: baidubce.BceClientConfiguration
        """
        bce_base_client.BceBaseClient.__init__(self, config)

    def _merge_config(self, config=None):
        """
        :param config:
        :type config: baidubce.BceClientConfiguration
        :return:
        """
        if config is None:
            return self.config
        else:
            new_config = copy.copy(self.config)
            new_config.merge_non_none_values(config)
            return new_config

    def _send_request(self, http_method, path,
                      body=None, headers=None, params=None,
                      config=None, body_parser=None):
        """
        :param http_method:
        :param path:
        :param body:
        :param headers:
        :param params:
        :param config:
        :param body_parser:
        :return:
        """
        config = self._merge_config(config)
        if body_parser is None:
            body_parser = handler.parse_json
        if headers is None:
            headers = {b'Accept': b'*/*', b'Content-Type':
                b'application/json;charset=utf-8'}

        return bce_http_client.send_request(
            config, bce_v1_signer.sign, [handler.parse_error, body_parser],
            http_method, path, body, headers, params)

    def create_et_dcphy(self, name, isp, intf_type, ap_type, ap_addr, user_name, user_phone, user_email,
                        user_idc, client_token=None, description=None, config=None):
        """
        Create new ET
        
        :param name: the name of ET
        :type name: string
        
        :param description: description of ET
        :type description: string
        
        :param isp: the type of isp
        :type isp: string
        
        :param intf_type: the type of interface
        :type intf_type: string
        
        :param ap_type: the type of ap
        :type ap_type: string
        
        :param ap_addr: the type of ap Address
        :type ap_addr: string
        
        :param user_name: the name of user
        :type user_name: string
        
        :param user_phone: phone number of user
        :type user_phone: string
        
        :param user_email: email of user
        :type user_email: string
        
        :param user_idc: Idc of user
        :type user_idc: string
        
        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: created ET ID
        """
        path = utils.append_uri(self.prefix, self.et, self.init)
        params = {}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token
        
        body = {
            'name': name,
            'description': description,
            'isp': isp,
            'intfType': intf_type,
            'apType': ap_type,
            'apAddr': ap_addr,
            'userName': user_name,
            'userPhone': user_phone,
            'userEmail': user_email,
            'userIdc': user_idc,
        }

        return self._send_request(http_methods.POST, path, body=json.dumps(body), params=params, config=config)

    def update_et_dcphy(self, et_id, name=None, description=None, user_name=None,
                        user_phone=None, user_email=None, client_token=None, config=None):
        """
        Update existed Et
        
        :param et_id: ET ID
        :type et_id: string
        
        :param user_name: the name of user
        :type user_name: string
        
        :param user_phone: phone number of user
        :type user_phone: string
        
        :param user_email: email of user
        :type user_email: string
        
        :param user_idc: Idc of user
        :type user_idc: string
        
        :param description: description of ET
        :type description: string
        
        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration
        """
        path = utils.append_uri(self.prefix, self.et, et_id)
        params = {}
        if name is not None:
            params[b'name'] = name
        if description is not None:
            params[b'description'] = description
        if user_name is not None:
            params[b'userName'] = user_name
        if user_phone is not None:
            params[b'userPhone'] = user_phone
        if user_email is not None:
            params[b'userEmail'] = user_email

        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        return self._send_request(http_methods.PUT, path, params=params, config=config)

    def list_et_dcphy(self, status=None, marker=None, max_keys=1000, config=None):
        """
        get a list of ETs owned by the authenticated user and specified
        conditions.

        :type status: string
        :param status: status of ET condition, if query by the status condition, must provides

        :type marker: string
        :param marker: The optional parameter marker specified in the original
         request to specify where in the results to begin listing.

        :type max_keys: int
        :param max_keys: The optional parameter to specifies the max number
         of list result to return. The default value is 1000.

        :type config: baidubce.BceClientConfiguration
        :param config:

        :return: list of ET model, for example:
                {
                    "nextMarker": "dcphy-gq65bz9ip712",
                    "marker": "dcphy-gq65bz9ie712",
                    "maxKeys": 1,
                    "isTruncated": true,
                    "ets": [{
                        "id": "dcphy-jy1sbnx32ez0",
                        "name": "et_6",
                        "description": "描述",
                        "status": "established",
                        "expireTime": "2019-01-30T08:50:00Z",
                        "isp": "ISP_CTC",
                        "intfType": "10G",
                        "apType": "BAIDU",
                        "apAddr": "BB",
                        "userName": "张三",
                        "userPhone": "133*****333",
                        "userEmail": "1***@123.com",
                        "userIdc": "北京|市辖区|东城区|2321"
                    }]
                }
        """
        path = utils.append_uri(self.prefix, self.et)
        params = {}
        if status is not None and isinstance(status, ETStatus):
            params[b'status'] = status.value
        if marker is not None:
            params[b'marker'] = marker
        if max_keys is not None:
            params[b'maxKeys'] = max_keys
        return self._send_request(http_methods.GET, path, params=params,
                                  config=config)

    def list_et_dcphy_detail(self, et_id, config=None):
        """
        List specific ET detail
        
        :param et_id: ET ID
        :type et_id: string
        
        :param config:
        :type config: baidubce.BceClientConfiguration

        :return: list of ET model, for example:
                {
                    "id": "dcphy-gq65bz9ip712",
                    "name": "ZX051501-testET",
                    "description": "",
                    "status": "established",
                    "expireTime": 1,
                    "isp": "ISP_CUCC",
                    "intfType": "10G",
                    "apType": "SINGLE",
                    "apAddr": "WHGG",
                    "userName": "张三",
                    "userPhone": "133*****333",
                    "userEmail": "1***@123.com",
                    "userIdc": "北京|市辖区|东城区|百度科技园K2"
                }
        """
        path = utils.append_uri(self.prefix, self.et, et_id)

        return self._send_request(http_methods.GET, path, config=config)

    def create_et_channel(self, et_id, local_ip, name, remote_ip, route_type, vlan_id,
                          authorized_users=None, description=None, networks=None,
                          bgp_asn=None, bgp_key=None, enable_ipv6=None, local_ipv6=None,
                          remote_ipv6=None, ipv6_networks=None, client_token=None, config=None):
        """
        Create new et channel
        
        :param et_id: et id
        :type et_id: string
        
        :param local_ip: baidu IPv4 address of et channel
        :type local_ip: string

        :param name: et channel name
        :type name: string

        :param networks: et channel IPv4 routes
        :type networks: list<string>

        :param remote_ip: customer IPv4 address of et channel
        :type remote_ip: string
        
        :param description: description of channel
        :type description: string
        
        :param route_type: et channel route type
        :value "bgp" or "static"
        :type route_type: string
        
        :param vlan_id: et channel vlan id
        :value 0, 2-4009
        :type vlan_id: integer
        
        :param authorized_users: et channel authorized users
        :type authorized_users: list<string>
        
        :param enable_ipv6: et channel enable ipv6
        :value 0 or 1
        :type enable_ipv6: integer

        :param local_ipv6: baidu IPv6 address of et channel
        :type local_ipv6: string

        :param remote_ipv6: customer IPv6 address of et channel
        :type remote_ipv6: string

        :param ipv6_networks: et channel IPv6 routes
        :type ipv6_networks: list<string>
        
        :param bgp_asn: BGP ASN
        :type bgp_asn: string
        
        :param bgp_key: BGP KEY
        :type bgp_key: string
        
        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel)
        params = {}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token
        
        body = {
            'etId': et_id,
            'baiduAddress': local_ip,
            'name': name,
            'customerAddress': remote_ip,
            'routeType': route_type,
            'vlanId': vlan_id,
        }

        if networks is not None:
            body['networks'] = networks

        if authorized_users is not None:
            body['authorizedUsers'] = authorized_users

        if description is not None:
            body['description'] = compat.convert_to_string(description)

        if enable_ipv6 is not None:
            body['enableIpv6'] = enable_ipv6

        if local_ipv6 is not None:
            body['baiduIpv6Address'] = compat.convert_to_string(local_ipv6)

        if remote_ipv6 is not None:
            body['customerIpv6Address'] = compat.convert_to_string(remote_ipv6)

        if ipv6_networks is not None:
            body['ipv6Networks'] = ipv6_networks

        return self._send_request(http_methods.POST, path, body=json.dumps(body), params=params, config=config)
    
    @required(et_id=(bytes, str))
    def get_et_channel(self, et_id, client_token=None, config=None):
        """
        Get et channel.
 
        :param et_id:
            et id
        :type et_id: string

        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel)
        params = {}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        return self._send_request(http_methods.GET, path, params=params, config=config)
    
    @required(et_id=(bytes, str), et_channel_id=(bytes, str), local_ip=(bytes, str), name=(bytes, str),
              networks=(list, tuple), remote_ip=(bytes, str), route_type=(bytes, str), vlan_id=int)
    def recommit_et_channel(self, et_id, et_channel_id, local_ip, name, networks, remote_ip, route_type, vlan_id,
                            authorized_users=None, description=None, enable_ipv6=None, local_ipv6=None,
                            remote_ipv6=None, ipv6_networks=None, client_token=None, config=None):
        """
        Recommit et channel.
 
        :param et_id:
            et id
        :type et_id: string

        :param et_channel_id:
            et channel id
        :type et_channel_id: string

        :param local_ip:
            baidu IPv4 address of et channel
        :type local_ip: string

        :param name:
            et channel name
        :type name: string

        :param networks:
            et channel IPv4 routes
        :type networks: list<string>

        :param remote_ip:
            customer IPv4 address of et channel
        :type remote_ip: string

        :param route_type:
            et channel route type
        :value "bgp" or "static"
        :type route_type: string

        :param vlan_id:
            et channel vlan id
        :value 0, 2-4009
        :type vlan_id: integer

        :param authorized_users:
            et channel authorized users
        :type authorized_users: list<string>

        :param description:
            et channel description
        :type description: string

        :param enable_ipv6:
            et channel enable ipv6
        :value 0 or 1
        :type enable_ipv6: integer

        :param local_ipv6:
            baidu IPv6 address of et channel
        :type local_ipv6: string

        :param remote_ipv6:
            customer IPv6 address of et channel
        :type remote_ipv6: string

        :param ipv6_networks:
            et channel IPv6 routes
        :type ipv6_networks: list<string>

        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id)
        params = {b'reCreate':None}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token
        
        body = {
            'baiduAddress': local_ip,
            'name': name,
            'networks': networks,
            'customerAddress': remote_ip,
            'routeType': route_type,
            'vlanId': vlan_id,
        }

        if authorized_users is not None:
            body['authorizedUsers'] = authorized_users

        if description is not None:
            body['description'] = compat.convert_to_string(description)

        if enable_ipv6 is not None:
            body['enableIpv6'] = enable_ipv6

        if local_ipv6 is not None:
            body['baiduIpv6Address'] = compat.convert_to_string(local_ipv6)

        if remote_ipv6 is not None:
            body['customerIpv6Address'] = compat.convert_to_string(remote_ipv6)

        if ipv6_networks is not None:
            body['ipv6Networks'] = ipv6_networks

        return self._send_request(http_methods.PUT, path, params=params, body=json.dumps(body), config=config)
    
    @required(et_id=(bytes, str), et_channel_id=(bytes, str))
    def update_et_channel(self, et_id, et_channel_id, name=None, description=None, client_token=None, config=None):
        """
        update et channel.
 
        :param et_id:
            et id
        :type et_id: string

        :param et_channel_id:
            et channel id
        :type et_channel_id: string

        :param name:
            et channel name
        :type name: string

        :param description:
            et channel description
        :type description: string

        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id)
        params = {b'modifyAttribute': None}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token
        
        body = {}

        if name is not None:
            body['name'] = compat.convert_to_string(name)

        if description is not None:
            body['description'] = compat.convert_to_string(description)

        return self._send_request(http_methods.PUT, path, params=params, body=json.dumps(body), config=config)
    
    @required(et_id=(bytes, str), et_channel_id=(bytes, str))
    def delete_et_channel(self, et_id, et_channel_id, client_token=None, config=None):
        """
        Delete et channel.
 
        :param et_id:
            et id
        :type et_id: string

        :param et_channel_id:
            et channel id
        :type et_channel_id: string

        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id)
        params = {}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        return self._send_request(http_methods.DELETE, path, params=params, config=config)
    
    @required(et_id=(bytes, str), et_channel_id=(bytes, str),
              local_ipv6=(bytes, str), remote_ipv6=(bytes, str))
    def enable_et_channel_ipv6(self, et_id, et_channel_id, local_ipv6, remote_ipv6,
                               ipv6_networks=None, client_token=None, config=None):
        """
        Enable IPv6 of the specific et channel.
 
        :param et_id:
            et id
        :type et_id: string

        :param et_channel_id:
            et channel id
        :type et_channel_id: string

        :param local_ipv6:
            baidu IPv6 address of et channel
        :type local_ipv6: string

        :param remote_ipv6:
            customer IPv6 address of et channel
        :type remote_ipv6: string

        :param ipv6_networks:
            et channel IPv6 routes
        :type ipv6_networks: list<string>

        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id)
        params = {b'enableIpv6': None}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token
        
        body = {
            'baiduIpv6Address': compat.convert_to_string(local_ipv6),
            'customerIpv6Address': compat.convert_to_string(remote_ipv6),
        }

        if ipv6_networks is not None:
            body['ipv6Networks'] = ipv6_networks

        return self._send_request(http_methods.PUT, path, params=params, body=json.dumps(body), config=config)

    @required(et_id=(bytes, str), et_channel_id=(bytes, str))
    def disable_et_channel_ipv6(self, et_id, et_channel_id, client_token=None, config=None):
        """
        Disable IPv6 of the specific et channel.
 
        :param et_id:
            The id of et.
        :type et_id: string

        :param et_channel_id:
            The id of et channel.
        :type et_channel_id: string

        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id)
        params = {b'disableIpv6': None}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        return self._send_request(http_methods.PUT, path, params=params, config=config)

    @required(et_id=(bytes, str), et_channel_id=(bytes, str))
    def create_et_channel_route_rule(self, et_id, et_channel_id, dest_address, nexthop_type,
                                     nexthop_id, description=None, ip_version=4,
                                     client_token=None, config=None):
        """
        Create a et channel route rule with the specified options.
        
        :param et_id:
            The id of et.
        :type et_id: string
        
        :param et_channel_id:
            The id of et channel.
        :type et_channel_id: string
        
        :param dest_address:
            Destination address of the route rule.
        :type dest_address: string
        
        :param nexthop_type
            The type of nexthop. 
            Value can be "etGateway" or "etChannel", respectively dedicated gateway, dedicated channel type.
        :type nexthop_type: string
        
        :param nexthop_id:
            The id of nexthop instance.
        :type nexthop_id: string
        
        :param description:
            The description of the route rule.
        :type description: string
        
        :param ip_version:
            The ip version of the route rule.
            Value can be 4 or 6, default 4.
        :type ip_version: int
 
        :param client_token:
            An ASCII string whose length is less than 64.
            The request will be idempotent if clientToken is provided.
            If the clientToken is not specified by the user,
            a random String generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id, self.route, self.rule)
        params = {}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        body = {
            'ipVersion': ip_version,
            'destAddress': compat.convert_to_string(dest_address),
            'nexthopType': compat.convert_to_string(nexthop_type),
            'nexthopId': compat.convert_to_string(nexthop_id),
        }
        
        if description is not None:
            body['description'] = compat.convert_to_string(description)

        return self._send_request(http_methods.POST, path, body=json.dumps(body), params=params,
                                  config=config)

    @required(et_id=(bytes, str), et_channel_id=(bytes, str), 
              marker=(bytes, str), max_Keys=int, dest_address=(bytes, str))
    def list_et_channel_route_rules(self, et_id, et_channel_id, marker=None,
                                    max_Keys=None, dest_address=None, config=None):
        """
        Return a list of route rules specifying the et channel

        :param et_id:
            The id of et.
        :type et_id: string

        :param et_channel_id:
            The id of et channel.
        :type et_channel_id: string

        :param marker:
            The optional parameter marker specified in the original request to specify
            where in the results to begin listing.
            Together with the marker, specifies the list result which listing should begin.
            If the marker is not specified, the list result will listing from the first one.
        :type marker: string

        :param max_keys:
            The optional parameter to specifies the max number of list result to return.
            The default value is 1000.
        :type max_keys: int

        :param dest_address:
            Destination address of the route rule.
        :type dest_address: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id, self.route, self.rule)
        params = {}

        if marker is not None:
            params[b'marker'] = marker
        if max_Keys is not None:
            params[b'maxKeys'] = max_Keys
        if dest_address is not None:
            params[b'destAddress'] = dest_address

        return self._send_request(http_methods.GET, path, params=params, config=config)

    @required(et_id=(bytes, str), et_channel=(bytes, str), et_channel_route_rule_id=(bytes, str))
    def update_et_channel_route_rule(self, et_id, et_channel_id, et_channel_route_rule_id,
                                     description, client_token=None, config=None):
        """
        Modify the special et channel route rule to new value.

        :param et_id:
            The id of et.
        :type et_id: string

        :param et_channel_id:
            The id of et channel.
        :type et_channel_id: string
        
        :param et_channel_route_rule_id:
            The id of et channel route rule.
        :type et_channel_route_rule_id: string

        :param description:
            The description of the route rule.
        :type description: string

        :param client_token:
            An ASCII string whose length is less than 64.
            The request will be idempotent if clientToken is provided.
            If the clientToken is not specified by the user,
            a random String generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel, et_channel_id,
                                self.route, self.rule, et_channel_route_rule_id)
        params = {}
        body = {
            'description': compat.convert_to_string(description)
        }

        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        return self._send_request(http_methods.PUT, path, json.dumps(body),
                                  params=params, config=config)

    @required(et_id=(bytes, str), et_channel_id=(bytes, str), et_channel_route_rule_id=(bytes, str))
    def delete_et_channel_route_rule(self, et_id, et_channel_id, et_channel_route_rule_id,
                                     client_token=None, config=None):
        """
        Delete the special et channel route rule to new value.

        :param et_id:
            The id of et.
        :type et_id: string

        :param et_channel_id:
            The id of et channel.
        :type et_channel_id: string
        
        :param et_channel_route_rule_id:
            The id of et channel route rule.
        :type et_channel_route_rule_id: string

        :param client_token:
            An ASCII string whose length is less than 64.
            The request will be idempotent if clientToken is provided.
            If the clientToken is not specified by the user, a random String generated by default algorithm will
            be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse
        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel, et_channel_id,
                                self.route, self.rule, et_channel_route_rule_id)
        params = {}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        return self._send_request(http_methods.DELETE, path, params=params, config=config)

    @required(et_id=(bytes, str), et_channel_id=(bytes, str), extra_channel_id=(bytes, str))
    def associate_et_channel(self, et_id, et_channel_id, extra_channel_id, client_token=None, config=None):
        """
        Associate et channel.

        :param et_id:
            et id
        :type et_id: string

        : param et_channel_id:
            et channel id
        :type et_channel_id: string

        :param extra_channel_id:
            extra channel id
        :type extra_channel_id: string

        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse

        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id)
        params = {b'associate': None}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        body = {
            'extraChannelId': compat.convert_to_string(extra_channel_id),
        }

        return self._send_request(http_methods.PUT, path, params=params, body=json.dumps(body), config=config)

    @required(et_id=(bytes, str), et_channel_id=(bytes, str), extra_channel_id=(bytes, str))
    def disassociate_et_channel(self, et_id, et_channel_id, extra_channel_id, client_token=None, config=None):
        """
        Disassociate et channel.

        :param et_id:
            et id
        :type et_id: string

        :param et_channel_id:
            et channel id
        :type et_channel_id: string

        :param extra_channel_id:
            extra channel id
        :type extra_channel_id: string

        :param client_token:
            If the clientToken is not specified by the user, a random String
            generated by default algorithm will be used.
        :type client_token: string

        :param config:
        :type config: baidubce.BceClientConfiguration

        :return:
        :rtype baidubce.bce_response.BceResponse

        """
        path = utils.append_uri(self.prefix, self.et, et_id, self.channel,
                                et_channel_id)
        params = {b'disassociate': None}
        if client_token is None:
            params[b'clientToken'] = generate_client_token()
        else:
            params[b'clientToken'] = client_token

        body = {
            'extraChannelId': compat.convert_to_string(extra_channel_id),
        }

        return self._send_request(http_methods.PUT, path, params=params, body=json.dumps(body), config=config)


def generate_client_token_by_uuid():
    """
    The default method to generate the random string for client_token
    if the optional parameter client_token is not specified by the user.

    :return:
    :rtype string
    """
    return str(uuid.uuid4())


generate_client_token = generate_client_token_by_uuid