Passed
Pull Request — master (#272)
by Vinicius
04:33
created

kytos.core.link.Link.is_active()   A

Complexity

Conditions 1

Size

Total Lines 11
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nop 1
dl 0
loc 11
rs 10
c 0
b 0
f 0
ccs 2
cts 2
cp 1
crap 1
1
"""Module with all classes related to links.
2
3
Links are low level abstractions representing connections between two
4
interfaces.
5
"""
6
7 1
import json
8 1
import random
9
10 1
from kytos.core.common import EntityStatus, GenericEntity
11 1
from kytos.core.exceptions import (KytosLinkCreationError,
12
                                   KytosNoTagAvailableError)
13 1
from kytos.core.id import LinkID
14 1
from kytos.core.interface import TAGType
15
16
17 1
class Link(GenericEntity):
18
    """Define a link between two Endpoints."""
19
20 1
    def __init__(self, endpoint_a, endpoint_b):
21
        """Create a Link instance and set its attributes.
22
23
        Two kytos.core.interface.Interface are required as parameters.
24
        """
25 1
        if endpoint_a is None:
26 1
            raise KytosLinkCreationError("endpoint_a cannot be None")
27 1
        if endpoint_b is None:
28 1
            raise KytosLinkCreationError("endpoint_b cannot be None")
29 1
        self.endpoint_a = endpoint_a
30 1
        self.endpoint_b = endpoint_b
31 1
        self._id = LinkID(endpoint_a.id, endpoint_b.id)
32 1
        super().__init__()
33
34 1
    def __hash__(self):
35
        return hash(self.id)
36
37 1
    def __repr__(self):
38 1
        return f"Link({self.endpoint_a!r}, {self.endpoint_b!r})"
39
40 1
    @property
41 1
    def status(self):
42
        """Return the current status of the Entity."""
43 1
        state = super().status
44 1
        if state == EntityStatus.DISABLED:
45 1
            return state
46
47 1
        for status_func in self.status_funcs.values():
48 1
            if status_func(self) == EntityStatus.DOWN:
49 1
                return EntityStatus.DOWN
50 1
        return state
51
52 1
    def is_enabled(self):
53
        """Override the is_enabled method.
54
55
        We consider a link enabled when all the interfaces are enabled.
56
57
        Returns:
58
            boolean: True if both interfaces are enabled, False otherwise.
59
60
        """
61 1
        return (self._enabled and self.endpoint_a.is_enabled() and
62
                self.endpoint_b.is_enabled())
63
64 1
    def is_active(self):
65
        """Override the is_active method.
66
67
        We consider a link active whether all the interfaces are active.
68
69
        Returns:
70
            boolean: True if the interfaces are active, othewrise False.
71
72
        """
73 1
        return (self._active and self.endpoint_a.is_active() and
74
                self.endpoint_b.is_active())
75
76 1
    def __eq__(self, other):
77
        """Check if two instances of Link are equal."""
78 1
        return self.id == other.id
79
80 1
    @property
81 1
    def id(self):  # pylint: disable=invalid-name
82
        """Return id from Link intance.
83
84
        Returns:
85
            string: link id.
86
87
        """
88 1
        return self._id
89
90 1
    @property
91 1
    def available_tags(self):
92
        """Return the available tags for the link.
93
94
        Based on the endpoint tags.
95
        """
96 1
        return [tag for tag in self.endpoint_a.available_tags if tag in
97
                self.endpoint_b.available_tags]
98
99 1
    def use_tag(self, tag):
100
        """Remove a specific tag from available_tags if it is there.
101
102
        Deprecated: use only the get_next_available_tag method.
103
        """
104 1
        if self.is_tag_available(tag):
105 1
            self.endpoint_a.use_tag(tag)
106 1
            self.endpoint_b.use_tag(tag)
107 1
            return True
108 1
        return False
109
110 1
    def is_tag_available(self, tag):
111
        """Check if a tag is available."""
112 1
        return (self.endpoint_a.is_tag_available(tag) and
113
                self.endpoint_b.is_tag_available(tag))
114
115 1
    def get_next_available_tag(self):
116
        """Return the next available tag if exists."""
117
        # Copy the available tags because in case of error
118
        # we will remove and add elements to the available_tags
119 1
        available_tags_a = self.endpoint_a.available_tags.copy()
120 1
        available_tags_b = self.endpoint_b.available_tags.copy()
121 1
        random.shuffle(available_tags_a)
122 1
        random.shuffle(available_tags_b)
123
124 1
        for tag in available_tags_a:
125
            # Tag does not exist in endpoint B. Try another tag.
126 1
            if tag not in available_tags_b:
127
                continue
128
129
            # Tag already in use. Try another tag.
130 1
            if not self.endpoint_a.use_tag(tag):
131
                continue
132
133
            # Tag already in use in B. Mark the tag as available again.
134 1
            if not self.endpoint_b.use_tag(tag):
135
                self.endpoint_a.make_tag_available(tag)
136
                continue
137
138
            # Tag used successfully by both endpoints. Returning.
139 1
            return tag
140
141
        raise KytosNoTagAvailableError(self)
142
143 1
    def make_tag_available(self, tag):
144
        """Add a specific tag in available_tags."""
145 1
        if not self.is_tag_available(tag):
146 1
            self.endpoint_a.make_tag_available(tag)
147 1
            self.endpoint_b.make_tag_available(tag)
148 1
            return True
149 1
        return False
150
151 1
    def available_vlans(self):
152
        """Get all available vlans from each interface in the link."""
153 1
        vlans_a = self._get_available_vlans(self.endpoint_a)
154 1
        vlans_b = self._get_available_vlans(self.endpoint_b)
155 1
        return [vlan for vlan in vlans_a if vlan in vlans_b]
156
157 1
    @staticmethod
158 1
    def _get_available_vlans(endpoint):
159
        """Return all vlans from endpoint."""
160 1
        tags = endpoint.available_tags
161 1
        return [tag for tag in tags if tag.tag_type == TAGType.VLAN]
162
163 1
    def as_dict(self):
164
        """Return the Link as a dictionary."""
165
        return {'id': self.id,
166
                'endpoint_a': self.endpoint_a.as_dict(),
167
                'endpoint_b': self.endpoint_b.as_dict(),
168
                'metadata': self.get_metadata_as_dict(),
169
                'active': self.is_active(),
170
                'enabled': self.is_enabled()}
171
172 1
    def as_json(self):
173
        """Return the Link as a JSON string."""
174
        return json.dumps(self.as_dict())
175
176 1
    @classmethod
177 1
    def from_dict(cls, link_dict):
178
        """Return a Link instance from python dictionary."""
179
        return cls(link_dict.get('endpoint_a'),
180
                   link_dict.get('endpoint_b'))
181