#New libraries added by Huawei start.
import netaddr
#New libraries added by Huawei end.

class FirewallRule(model_base.BASEV2, models_v2.HasId, models_v2.HasTenant):
    """Represents a Firewall rule."""
    __tablename__ = 'firewall_rules'
    __table_args__ = ({'mysql_collate': 'utf8_bin'})
    name = sa.Column(sa.String(255))
    description = sa.Column(sa.String(1024))
    firewall_policy_id = sa.Column(sa.String(36),
                                   sa.ForeignKey('firewall_policies.id'),
                                   nullable=True)
    shared = sa.Column(sa.Boolean)
    protocol = sa.Column(sa.String(40))
    ip_version = sa.Column(sa.Integer, nullable=False)
    source_ip_address = sa.Column(sa.String(46))
    destination_ip_address = sa.Column(sa.String(46))
    #Code block added by huawei starts.
    #Add four parameters to FirewallRule.
    source_ip_addresses = sa.Column(sa.PickleType(protocol=2), nullable=True)
    destination_ip_addresses = sa.Column(sa.PickleType(protocol=2), nullable=True)
    source_ports = sa.Column(sa.String(255))
    destination_ports = sa.Column(sa.String(255))    
    #Code block added by huawei ends.
    
def _make_firewall_rule_dict(self, firewall_rule, fields=None):
    position = None
    # We return the position only if the firewall_rule is bound to a
    # firewall_policy.
    if firewall_rule['firewall_policy_id']:
        position = firewall_rule['position']
    src_port_range = self._get_port_range_from_min_max_ports(
        firewall_rule['source_port_range_min'],
        firewall_rule['source_port_range_max'])
    dst_port_range = self._get_port_range_from_min_max_ports(
        firewall_rule['destination_port_range_min'],
        firewall_rule['destination_port_range_max'])
    res = {'id': firewall_rule['id'],
            #Code block added by huawei starts.
            #Add four parameters to res.
           'source_ip_addresses': firewall_rule['source_ip_addresses'],
           'destination_ip_addresses':firewall_rule['destination_ip_addresses'],
           'source_ports': firewall_rule['source_ports'],
           'destination_ports': firewall_rule['destination_ports'],
            #Code block added by huawei ends.

def _get_min_max_ports_from_range(self, port_range):
	if not port_range:
		return [None, None]
	min_port, sep, max_port = port_range.partition(":")
	if not max_port:
		max_port = min_port
	#Code block added by huawei starts.
	self._validate_fwr_port_range(min_port, max_port)
	#Code block added by huawei ends.
	return [int(min_port), int(max_port)]
	
def _get_port_range_from_min_max_ports(self, min_port, max_port):
	if not min_port:
		return None
	if min_port == max_port:
		return str(min_port)
	#Code block added by huawei starts.
	self._validate_fwr_port_range(min_port, max_port)
	#Code block added by huawei ends.
	return '%s:%s' % (min_port, max_port)
	
# A new  method added by Huawei starts.
#used to get source ports or destination ports from firewall rule.
def _get_ports(self, fwr, type): 
    ports = None
    port = None
    msg = ''
    
    if type == "source":
        msg = "Source-port and source-ports"
        
        if 'source_port' in fwr and fwr['source_port'] is not None :
            port = fwr['source_port'];
            
        if 'source_ports' in fwr and fwr['source_ports'] is not None :
            ports = fwr['source_ports'];  
            
    if type == "destination":
        msg = "Destination-port and destination-ports"
        
        if 'destination_port' in fwr and fwr['destination_port'] is not None :
            port = fwr['destination_port'];
            
        if 'destination_ports' in fwr and fwr['destination_ports'] is not None :
            ports = fwr['destination_ports'];
            
    if port is not None and ports is not None:
        raise fw_ext.FirewallRulePropertyConflict(
                param=msg)
        
    if ports is not None:
        
        portList = ports.split(',')
        if len(portList) > 15:
            raise fw_ext.FirewallRulePortsLengthMax(
                param=type)
        list = []
        for singlePort in portList:
            min_port, sep, max_port = singlePort.partition(":")
            if not max_port:
                list.append(min_port)
            else:
                self._validate_fwr_port_range(min_port, max_port)
                list.append(min_port + ":" + max_port)
        return ','.join(list)
# A new  method added by Huawei ends.
       
# A new  method added by Huawei starts.
#used to get source ip addresses or destination ip addresses from firewall rule.
def _get_source_or_destination_ip_addresses(self, fwr, type):
    ip_address = None
    ip_addresses = None
    msg = ' '

    if type == "source":
        msg = "Source-ip-address and source-ip-addresses"
        
        if 'source_ip_address' in fwr and fwr['source_ip_address'] is not None :
            ip_address = fwr['source_ip_address']
            
        if 'source_ip_addresses' in fwr and fwr['source_ip_addresses'] is not None :
            ip_addresses = fwr['source_ip_addresses']
            
            
    if type == "destination":
        msg = "Destination-ip-address and destination-ip-addresses"
        
        if 'destination_ip_address' in fwr and fwr['destination_ip_address'] is not None :
            ip_address = fwr['destination_ip_address'];
            
        if 'destination_ip_addresses' in fwr and fwr['destination_ip_addresses'] is not None :
            ip_addresses = fwr['destination_ip_addresses'];
                
    if ip_address == '':
        ip_address = None
        
    if ip_addresses == '':
        ip_addresses = None 
        
    if ip_address is not None and ip_addresses is not None:
        raise fw_ext.FirewallRulePropertyConflict(
            param=msg)
              
    if ip_addresses is not None:
        addresses = ip_addresses.split(',')
        if len(addresses) > 800:
            raise fw_ext.FirewallRuleIpAddressLengthMax(
                param=type)
        else:    
            return ip_addresses
                
# A new  method added by Huawei ends.

# A new  method added by Huawei starts.
#used to determine whether ip address in source ip addresses or destination ip addresses are duplicated.
def _validate_fwr_src_dst_ips_duplicate(self, fwr):
    if 'source_ip_addresses' in fwr and fwr['source_ip_addresses'] is not None:
        sourceIpAddressList = fwr['source_ip_addresses'].split(',')
        if (len(sourceIpAddressList) > 1 ) and (len(sourceIpAddressList) != len(set(sourceIpAddressList))):
            raise fw_ext.FirewallRuleIpAddressDuplicate(
                param='source-ip-addresses')
    if 'destination_ip_addresses' in fwr and fwr['destination_ip_addresses'] is not None:
        destinationIpAddressList = fwr['destination_ip_addresses'].split(',')
        if (len(destinationIpAddressList) > 1) and (len(destinationIpAddressList) != len(set(destinationIpAddressList))):
            raise fw_ext.FirewallRuleIpAddressDuplicate(
                param='destination-ip-addresses')
# A new  method added by Huawei ends.
        
# A new  method added by Huawei starts.
#used to verify ip addresses version.
def _validate_fwr_ip_addresses_version(self, ipAddrs):
    ipAddrList = ipAddrs.split(',')
    versionList = []
    for ipAddr in ipAddrList:
        if ipAddr:
            versionList.append(netaddr.IPNetwork(ipAddr).version) 
    if len(set(versionList)) > 1:
        raise fw_ext.FirewallIpAddressConflict()
    else:
        return versionList
# A new  method added by Huawei ends.
                
# A new  method added by Huawei starts.
#used to verify ip address version.
def _validate_fwr_src_dst_ip_version(self, fwr):
    src_version = dst_version = None
    if fwr['source_ip_address']:
        src_version = netaddr.IPNetwork(fwr['source_ip_address']).version
    if fwr['destination_ip_address']:
        dst_version = netaddr.IPNetwork(
            fwr['destination_ip_address']).version
    rule_ip_version = fwr['ip_version']
      
    srcsVersionList = [];
    if fwr['source_ip_addresses']:
        srcsVersionList = self._validate_fwr_ip_addresses_version(fwr['source_ip_addresses'])
     
    destsVersionList = [];         
    if fwr['destination_ip_addresses']:
        destsVersionList = self._validate_fwr_ip_addresses_version(fwr['destination_ip_addresses'])
    
    if srcsVersionList and len(srcsVersionList) > 0:
        src_version = srcsVersionList[0]
        
    if destsVersionList and len(destsVersionList) > 0:
        dst_version = destsVersionList[0]
        
    if (src_version and dst_version and src_version != dst_version):
        raise fw_ext.FirewallIpAddressConflict()
    
    if src_version :
            fwr['ip_version'] = src_version
            
    if dst_version :
        fwr['ip_version'] = dst_version
# A new  method added by Huawei ends.

# A new  method added by Huawei starts.
#used to validate port range.
def _validate_fwr_port_range(self, min_port, max_port):
	if int(min_port) > int(max_port):
		port_range = '%s:%s' % (min_port, max_port)
		raise fw_ext.FirewallRuleInvalidPortValue(port=port_range)
# A new  method added by Huawei ends.
    
def create_firewall_rule(self, context, firewall_rule):
    LOG.debug("create_firewall_rule() called")
    fwr = firewall_rule['firewall_rule']
    self._validate_fwr_protocol_parameters(fwr)
	#Code block modified by huawei starts. 
    self._validate_fwr_src_dst_ip_version(fwr)
    self._validate_fwr_src_dst_ips_duplicate(fwr)
    #Code block modified by huawei ends. 
    
    if not fwr['protocol'] and (fwr['source_port'] or
            fwr['destination_port']):
        raise fw_ext.FirewallRuleWithPortWithoutProtocolInvalid()
    src_port_min, src_port_max = self._get_min_max_ports_from_range(fwr['source_port'])
    dst_port_min, dst_port_max = self._get_min_max_ports_from_range(fwr['destination_port'])
    #Code block modified by huawei starts. 
    src_ports = self._get_ports(fwr, "source")
    dst_ports = self._get_ports(fwr, "destination")
    source_ip_addresses = self._get_source_or_destination_ip_addresses(fwr, "source")
    destination_ip_addresses = self._get_source_or_destination_ip_addresses(fwr, "destination")
    #Code block modified by huawei ends.
    with context.session.begin(subtransactions=True):
            fwr_db = FirewallRule(
                #Code block modified by huawei start. 
                source_ip_addresses=source_ip_addresses,
                destination_ip_addresses=destination_ip_addresses,
                source_ports=src_ports, 
                destination_ports=dst_ports, 
                #Code block modified by huawei end.
                
def update_firewall_rule(self, context, id, firewall_rule):
    LOG.debug("update_firewall_rule() called")
    fwr = firewall_rule['firewall_rule']
    #Code block modified by huawei starts. 
    if 'source_ip_addresses' in fwr and fwr['source_ip_addresses']:
        self._validate_fwr_ip_addresses_version(fwr['source_ip_addresses'])
        
    if 'destination_ip_addresses' in fwr and fwr['destination_ip_addresses']:
        self._validate_fwr_ip_addresses_version(fwr['destination_ip_addresses'])
    self._validate_fwr_src_dst_ips_duplicate(fwr)
    #Code block modified by huawei ends. 
    
    fwr_db = self._get_firewall_rule(context, id)

    if fwr_db.firewall_policy_id:
        fwp_db = self._get_firewall_policy(context,
                                           fwr_db.firewall_policy_id)
        if 'shared' in fwr and not fwr['shared']:
            if fwr_db['tenant_id'] != fwp_db['tenant_id']:
                raise fw_ext.FirewallRuleInUse(firewall_rule_id=id)
                
    if 'source_port' in fwr:
        src_port_min, src_port_max = self._get_min_max_ports_from_range(fwr['source_port'])
        fwr['source_port_range_min'] = src_port_min
        fwr['source_port_range_max'] = src_port_max
        del fwr['source_port']
        #Code block added by huawei starts. 
        fwr_db['source_ports'] = ''
        #Code block added by huawei ends. 
        
    if 'destination_port' in fwr:
        dst_port_min, dst_port_max = self._get_min_max_ports_from_range(
            fwr['destination_port'])
        fwr['destination_port_range_min'] = dst_port_min
        fwr['destination_port_range_max'] = dst_port_max
        del fwr['destination_port']
        #Code block added by huawei starts. 
        fwr_db['destination_ports'] = ''
    
    if 'source_ports' in fwr:
        fwr['source_ports'] = self._get_ports(fwr, "source")
        fwr_db['source_port_range_min'] = None
        fwr_db['source_port_range_max'] = None
        
        
    if 'destination_ports' in fwr:
        fwr['destination_ports'] = self._get_ports(fwr, "destination")
        fwr_db['destination_port_range_min'] = None
        fwr_db['destination_port_range_max'] = None
        
        
    if 'source_ip_addresses' in fwr:
        fwr['source_ip_addresses'] = self._get_source_or_destination_ip_addresses(fwr, "source")
        fwr_db['source_ip_address'] = ''
        
    if 'destination_ip_addresses' in fwr:
        fwr['destination_ip_addresses'] = self._get_source_or_destination_ip_addresses(fwr, "destination")
        fwr_db['destination_ip_address'] = ''
    
    if 'source_ip_address' in fwr:
        fwr_db['source_ip_addresses'] = ''
        
    if 'destination_ip_address' in fwr:
        fwr['destination_ip_addresses'] = ''
    #Code block added by huawei ends. 
        
    with context.session.begin(subtransactions=True):
        protocol = fwr.get('protocol', fwr_db['protocol'])
        if not protocol:
            sport = fwr.get('source_port_range_min',
                            fwr_db['source_port_range_min'])
            dport = fwr.get('destination_port_range_min',
                            fwr_db['destination_port_range_min'])
            #Code block modified by huawei starts. 
            if sport or dport or fwr['source_ports'] or fwr['destination_ports']:
            #Code block modified by huawei ends.     
                raise fw_ext.FirewallRuleWithPortWithoutProtocolInvalid()
        fwr_db.update(fwr)
        if fwr_db.firewall_policy_id:
            fwp_db.audited = False
    return self._make_firewall_rule_dict(fwr_db)