<commit_before>"""
Core abstractions used in OpenNSA.

In design pattern terms, these would be Data Transfer Objects (DTOs).
Though some of them do actually have some functionality methods.

Author: <NAME> <<EMAIL>>
Copyright: NORDUnet (2011-2013)
"""


import uuid
import random
import urlparse
import itertools

from opennsa import error, constants as cnt



LOG_SYSTEM = 'opennsa.nsa'

OGF_PREFIX = 'urn:ogf:network:'
URN_UUID_PREFIX = 'urn:uuid:'

BIDIRECTIONAL   = 'Bidirectional'



class NSIHeader(object):

    def __init__(self, requester_nsa, provider_nsa, session_security_attrs=None, correlation_id=None, reply_to=None):
        self.requester_nsa          = requester_nsa
        self.provider_nsa           = provider_nsa
        self.session_security_attrs = session_security_attrs
        self.reply_to               = reply_to
        self.correlation_id = correlation_id or self.createCorrelationId()

    def createCorrelationId(self):
        return URN_UUID_PREFIX + str(uuid.uuid1())

    def newCorrelationId(self):
        self.correlation_id = self.createCorrelationId()


    def __repr__(self):
        return '<NSIHeader: %s, %s, %s, %s, %s>' % (self.requester_nsa, self.provider_nsa, self.session_security_attrs, self.reply_to, self.correlation_id)



class EmptyLabelSet(Exception):
    pass



class Label(object):

    def __init__(self, type_, values=None):

        assert type(values) in (None, str, list), 'Type of Label values must be a None, str, or list. Was given %s' % type(values)

        self.type_ = type_
        self.values = self._parseLabelValues(values) if values is not None else None


    def _parseLabelValues(self, values):

        def createValue(value):
            try:
                if '-' in value:
                    v1, v2 = value.split('-', 1)
                    i1, i2 = int(v1), int(v2)
                    if i1 > i2:
                        raise error.PayloadError('Label value %s is in descending order, which is not allowed.' % value)
                else:
                    i1 = int(value)
                    i2 = i1
                return i1, i2
            except ValueError:
                raise error.PayloadError('Label %s is not an integer or an integer range.' % value)

        if type(values) is str:
            values = values.split(',')

        parsed_values = sorted( [ createValue(value) for value in values ] )

        # detect any overlap and remove it - remember that the list is sorted

        nv = [] # normalized values
        for v1, v2 in parsed_values:
            if len(nv) == 0:
                nv.append( (v1,v2) )
                continue

            l = nv[-1] # last
            if v1 <= l[1] + 1: # merge
                nv = nv[:-1] + [ (l[0], max(l[1],v2)) ]
            else:
                nv.append( (v1,v2) )

        return nv


    def intersect(self, other):
        # get the common labels between two label set - I hate you nml
        assert type(other) is Label, 'Cannot intersect label with something that is not a label (other was %s)' % type(other)
        assert self.type_ == other.type_, 'Cannot insersect label of different types'

        label_values = []
        i = iter(other.values)
        o1, o2 = i.next()

        for v1, v2 in self.values:
            while True:
                if v2 < o1:
                    break
                elif o2 < v1:
                    try:
                        o1, o2 = i.next()
                    except StopIteration:
                        break
                    continue
                label_values.append( ( max(v1,o1), min(v2,o2)) )
                if v2 <= o2:
                    break
                elif o2 <= v2:
                    try:
                        o1, o2 = i.next()
                    except StopIteration:
                        break

        if len(label_values) == 0:
            raise EmptyLabelSet('Label intersection produced empty label set')

        ls = ','.join( [ '%i-%s' % (nv[0], nv[1]) for nv in label_values ] )
        return Label(self.type_, ls)


    def labelValue(self):
        vs = [ str(v1) if v1 == v2 else str(v1) + '-' + str(v2) for v1,v2 in self.values ]
        return ','.join(vs)

    def singleValue(self):
        return len(self.values) == 1 and self.values[0][0] == self.values[0][1]

    def enumerateValues(self):
        lv = [ range(lr[0], lr[1]+1) for lr in self.values ]
        return list(itertools.chain.from_iterable( lv ) )

    def randomLabel(self):
        # not evenly distributed, but that isn't promised anyway
        label_range = random.choice(self.values)
        return random.randint(label_range[0], label_range[1]+1)


    def __eq__(self, other):
        if not type(other) is Label:
            return False
        return self.type_ == other.type_ and sorted(self.values) == sorted(other.values)


    def __repr__(self):
        return '<Label %s:%s>' % (self.type_, self.labelValue())



class STP(object): # Service Termination Point

    def __init__(self, network, port, labels=None):
        assert type(network) is str, 'Invalid network type provided for STP'
        assert type(port) is str, 'Invalid port type provided for STP'
        self.network = network
        self.port = port
        self.labels = labels or []


    def __eq__(self, other):
        if not type(other) is STP:
            return False
        return self.network == other.network and self.port == other.port and self.labels == other.labels


    def __repr__(self):
        base = '<STP %s %s' % (self.network, self.port)
        if self.labels:
            base += '#' + ','.join( [ label.type_.split('#')[-1] + '=' + label.labelValue() for label in self.labels ] )
        return base + '>'



class Link(object): # intra network link

    def __init__(self, network, src_port, dst_port, src_labels=None, dst_labels=None):
        if src_labels is None:
            assert dst_labels is None, 'Source and destination labels must either both be None, or both specified'
        else:
            assert dst_labels is not None, 'Source and destination labels must either both be None, or both specified'
            assert type(src_labels) is list, 'Source labels must be a list'
            assert type(dst_labels) is list, 'Dest labels must be a list'
        self.network = network
        self.src_port = src_port
        self.dst_port = dst_port
        self.src_labels = src_labels
        self.dst_labels = dst_labels


    def sourceSTP(self):
        return STP(self.network, self.src_port, None, self.src_labels)<commit_msg>use space to seperate port and labels in stp __repr__<commit_after>"""
Core abstractions used in OpenNSA.

In design pattern terms, these would be Data Transfer Objects (DTOs).
Though some of them do actually have some functionality methods.

Author: <NAME> <<EMAIL>>
Copyright: NORDUnet (2011-2013)
"""


import uuid
import random
import urlparse
import itertools

from opennsa import error, constants as cnt



LOG_SYSTEM = 'opennsa.nsa'

OGF_PREFIX = 'urn:ogf:network:'
URN_UUID_PREFIX = 'urn:uuid:'

BIDIRECTIONAL   = 'Bidirectional'



class NSIHeader(object):

    def __init__(self, requester_nsa, provider_nsa, session_security_attrs=None, correlation_id=None, reply_to=None):
        self.requester_nsa          = requester_nsa
        self.provider_nsa           = provider_nsa
        self.session_security_attrs = session_security_attrs
        self.reply_to               = reply_to
        self.correlation_id = correlation_id or self.createCorrelationId()

    def createCorrelationId(self):
        return URN_UUID_PREFIX + str(uuid.uuid1())

    def newCorrelationId(self):
        self.correlation_id = self.createCorrelationId()


    def __repr__(self):
        return '<NSIHeader: %s, %s, %s, %s, %s>' % (self.requester_nsa, self.provider_nsa, self.session_security_attrs, self.reply_to, self.correlation_id)



class EmptyLabelSet(Exception):
    pass



class Label(object):

    def __init__(self, type_, values=None):

        assert type(values) in (None, str, list), 'Type of Label values must be a None, str, or list. Was given %s' % type(values)

        self.type_ = type_
        self.values = self._parseLabelValues(values) if values is not None else None


    def _parseLabelValues(self, values):

        def createValue(value):
            try:
                if '-' in value:
                    v1, v2 = value.split('-', 1)
                    i1, i2 = int(v1), int(v2)
                    if i1 > i2:
                        raise error.PayloadError('Label value %s is in descending order, which is not allowed.' % value)
                else:
                    i1 = int(value)
                    i2 = i1
                return i1, i2
            except ValueError:
                raise error.PayloadError('Label %s is not an integer or an integer range.' % value)

        if type(values) is str:
            values = values.split(',')

        parsed_values = sorted( [ createValue(value) for value in values ] )

        # detect any overlap and remove it - remember that the list is sorted

        nv = [] # normalized values
        for v1, v2 in parsed_values:
            if len(nv) == 0:
                nv.append( (v1,v2) )
                continue

            l = nv[-1] # last
            if v1 <= l[1] + 1: # merge
                nv = nv[:-1] + [ (l[0], max(l[1],v2)) ]
            else:
                nv.append( (v1,v2) )

        return nv


    def intersect(self, other):
        # get the common labels between two label set - I hate you nml
        assert type(other) is Label, 'Cannot intersect label with something that is not a label (other was %s)' % type(other)
        assert self.type_ == other.type_, 'Cannot insersect label of different types'

        label_values = []
        i = iter(other.values)
        o1, o2 = i.next()

        for v1, v2 in self.values:
            while True:
                if v2 < o1:
                    break
                elif o2 < v1:
                    try:
                        o1, o2 = i.next()
                    except StopIteration:
                        break
                    continue
                label_values.append( ( max(v1,o1), min(v2,o2)) )
                if v2 <= o2:
                    break
                elif o2 <= v2:
                    try:
                        o1, o2 = i.next()
                    except StopIteration:
                        break

        if len(label_values) == 0:
            raise EmptyLabelSet('Label intersection produced empty label set')

        ls = ','.join( [ '%i-%s' % (nv[0], nv[1]) for nv in label_values ] )
        return Label(self.type_, ls)


    def labelValue(self):
        vs = [ str(v1) if v1 == v2 else str(v1) + '-' + str(v2) for v1,v2 in self.values ]
        return ','.join(vs)

    def singleValue(self):
        return len(self.values) == 1 and self.values[0][0] == self.values[0][1]

    def enumerateValues(self):
        lv = [ range(lr[0], lr[1]+1) for lr in self.values ]
        return list(itertools.chain.from_iterable( lv ) )

    def randomLabel(self):
        # not evenly distributed, but that isn't promised anyway
        label_range = random.choice(self.values)
        return random.randint(label_range[0], label_range[1]+1)


    def __eq__(self, other):
        if not type(other) is Label:
            return False
        return self.type_ == other.type_ and sorted(self.values) == sorted(other.values)


    def __repr__(self):
        return '<Label %s:%s>' % (self.type_, self.labelValue())



class STP(object): # Service Termination Point

    def __init__(self, network, port, labels=None):
        assert type(network) is str, 'Invalid network type provided for STP'
        assert type(port) is str, 'Invalid port type provided for STP'
        self.network = network
        self.port = port
        self.labels = labels or []


    def __eq__(self, other):
        if not type(other) is STP:
            return False
        return self.network == other.network and self.port == other.port and self.labels == other.labels


    def __repr__(self):
        base = '<STP %s %s' % (self.network, self.port)
        if self.labels:
            base += ' ' + ','.join( [ label.type_.split('#')[-1] + '=' + label.labelValue() for label in self.labels ] )
        return base + '>'



class Link(object): # intra network link

    def __init__(self, network, src_port, dst_port, src_labels=None, dst_labels=None):
        if src_labels is None:
            assert dst_labels is None, 'Source and destination labels must either both be None, or both specified'
        else:
            assert dst_labels is not None, 'Source and destination labels must either both be None, or both specified'
            assert type(src_labels) is list, 'Source labels must be a list'
            assert type(dst_labels) is list, 'Dest labels must be a list'
        self.network = network
        self.src_port = src_port
        self.dst_port = dst_port
        self.src_labels = src_labels
        self.dst_labels = dst_labels


    def sourceSTP(self):
        return STP(self.network, self.src_port, None, self.src_labels)
{'fraction_non_alphanumeric': 0.10157075970397221, 'fraction_numerical': 0.012082766953632381, 'mean_word_length': 2.0090888434446716, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '453918', 'n_tokens_mistral': 4042, 'n_tokens_neox': 3829, 'n_words': 1208}