blob: 54bf24f25fe21981762b8cf86382fc2526c955d6 [file] [log] [blame]
Chip Bolingf5af85d2019-02-12 15:36:17 -06001# Copyright 2017-present Adtran, Inc.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15from pyvoltha.common.openflow.utils import *
16from evc import EVC
17from flow_entry import FlowEntry
18from twisted.internet import defer
19from twisted.internet.defer import returnValue, inlineCallbacks
20
21log = structlog.get_logger()
22
23EVC_NAME_FORMAT = 'VOLTHA-MCAST-{}' # format(flow.vlan_id)
24EVC_NAME_REGEX_ALL = EVC_NAME_FORMAT.format('*')
25
26
27_mcast_evcs = {} # device-id -> flow dictionary
28 # |
29 # +-> vlan-id -> evcs
30
31
32class MCastEVC(EVC):
33 """
34 Class to wrap Multicast EVC and EVC-MAP functionality
35 """
36 def __init__(self, flow_entry):
37 super(MCastEVC, self).__init__(flow_entry)
38 self._downstream_flows = {flow_entry.flow_id} # Matching Downstream Flow IDs
39
40 def __str__(self):
41 return "MCAST-{}: MEN: {}, VLAN: {}".format(self._name, self._men_ports, self._s_tag)
42
43 def _create_name(self):
44 #
45 # TODO: Take into account selection criteria and output to make the name
46 #
47 return EVC_NAME_FORMAT.format(self._flow.vlan_id)
48
49 def _create_evc_map(self, flow_entry):
50 from evc_map import EVCMap
51 flow = FakeUpstreamFlow(flow_entry.flow, flow_entry.handler)
52 return EVCMap.create_ingress_map(flow, self)
53
54 @staticmethod
55 def create(flow_entry):
56 from evc_map import EVCMap
57
58 device_id = flow_entry.device_id
59 if device_id not in _mcast_evcs:
60 _mcast_evcs[device_id] = {}
61
62 evc_table = _mcast_evcs[device_id]
63
64 try:
65 evc = evc_table.get(flow_entry.vlan_id)
66
67 if evc is None:
68 # Create EVC and initial EVC Map
69 evc = MCastEVC(flow_entry)
70 evc_table[flow_entry.vlan_id] = evc
71 else:
72 if flow_entry.flow_id in evc.downstream_flows: # TODO: Debug only to see if flow_ids are unique
73 pass
74 else:
75 evc.add_downstream_flows(flow_entry.flow_id)
76
77 fake_flow = FakeUpstreamFlow(flow_entry.flow, flow_entry.handler)
78 evc_map_name = EVCMap.create_evc_map_name(fake_flow)
79
80 if evc_map_name not in evc.evc_map_names:
81 EVCMap.create_ingress_map(fake_flow, evc)
82
83 return evc
84
85 except Exception as e:
86 log.exception('mcast-create', e=e)
87 return None
88
89 @property
90 def flow_entry(self):
91 return self._flow
92
93 @property
94 def downstream_flows(self):
95 return frozenset(self._downstream_flows)
96
97 def add_downstream_flows(self, flow_id):
98 self._downstream_flows.add(flow_id)
99
100 def remove_downstream_flows(self, flow_id):
101 self._downstream_flows.discard(flow_id)
102
103 @inlineCallbacks
104 def remove(self, remove_maps=True):
105 """
106 Remove EVC (and optional associated EVC-MAPs) from hardware
107 :param remove_maps: (boolean)
108 :return: (deferred)
109 """
110 log.info('removing', evc=self, remove_maps=remove_maps)
111
112 device_id = self._handler.device_id
113 flow_id = self._flow.id
114 evc_table = _mcast_evcs.get(device_id)
115
116 if evc_table is None or flow_id not in evc_table:
117 returnValue('NOP')
118
119 # Remove flow reference
120 if self._flow.flow_id in self._downstream_flows:
121 del self._downstream_flows[self._flow.flow_id]
122
123 if len(self._downstream_flows) == 0:
124 # Use base class to clean up
125 returnValue(super(MCastEVC, self).remove(remove_maps=True))
126
127 returnValue('More references')
128
129 @inlineCallbacks
130 def delete(self, delete_maps=True):
131 """
132 Remove from hardware and delete/clean-up EVC Object
133 """
134 log.info('deleting', evc=self, delete_maps=delete_maps)
135
136 try:
137 dl = [self.remove()]
138 if delete_maps:
139 for evc_map in self.evc_maps:
140 dl.append(evc_map.delete(self)) # TODO: implement bulk-flow procedures
141
142 yield defer.gatherResults(dl, consumeErrors=True)
143
144 except Exception as e:
145 log.exception('removal', e=e)
146
147 self._evc_maps = None
148 f, self._flow = self._flow, None
149 if f is not None and f.handler is not None:
150 f.handler.remove_evc(self)
151
152 def reflow(self, reflow_maps=True):
153 pass # TODO: Implement or use base class?
154
155 @staticmethod
156 def remove_all(client, regex_=EVC_NAME_REGEX_ALL):
157 """
158 Remove all matching EVCs from hardware
159 :param client: (ncclient) NETCONF Client to use
160 :param regex_: (String) Regular expression for name matching
161 :return: (deferred)
162 """
163 pass # TODO: ???
164
165
166class FakeUpstreamFlow(FlowEntry):
167 def __init__(self, flow, handler):
168 super(FakeUpstreamFlow, self).__init__(flow, handler)
169 self._decode()
170 # Change name that the base class set
171 self._name = self.create_flow_name()
172 self._flow_direction = FlowEntry.FlowDirection.UPSTREAM
173 self.in_port, self.output = self.output, self.in_port
174 self.flow_id = '{}-MCAST'.format(self.vlan_id)
175 self._logical_port = self.vlan_id
176 self.push_vlan_id = self.vlan_id
177 self.vlan_id = None
178 self.signature = None
179 self.inner_vid = None
180 self.pop_vlan = False
181
182 def create_flow_name(self):
183 return 'flow-{}-{}-MCAST'.format(self.device_id, self.vlan_id)