blob: fc7fd0b6cb058ae4eecdf6b22406818e628435b0 [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 twisted.internet import defer
18from twisted.internet.defer import returnValue, inlineCallbacks
19
20log = structlog.get_logger()
21
22EVC_NAME_FORMAT = 'VOLTHA-UTILITY-{}' # format(flow.vlan_id)
23EVC_NAME_REGEX_ALL = EVC_NAME_FORMAT.format('*')
24
25
26_utility_evcs = {} # device-id -> flow dictionary
27 # |
28 # +-> utility-vlan-id -> evcs
29
30
31class UtilityEVC(EVC):
32 """
33 Class to wrap orphan ingress ACLs EVC functionality
34 """
35 def __init__(self, flow_entry):
36 super(UtilityEVC, self).__init__(flow_entry)
37 self._downstream_flows = {flow_entry.flow_id} # Matching Downstream Flow IDs
38 self.service_evc = True
39
40 def __str__(self):
41 return "VOLTHA-UTILITY-{}: MEN: {}, VLAN: {}".format(self._name, self._men_ports, self._s_tag)
42
43 def _create_name(self, vlan_id=None):
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 if vlan_id is None else vlan_id)
48
49 @staticmethod
50 def create(flow_entry, use_default_vlan_id=False):
51 device_id = flow_entry.device_id
52 vlan_id = flow_entry.vlan_id if not use_default_vlan_id else flow_entry.handler.utility_vlan
53 evc_table = _utility_evcs.get(device_id)
54
55 if evc_table is None:
56 _utility_evcs[device_id] = dict()
57 evc_table = _utility_evcs[device_id]
58
59 try:
60 evc = evc_table.get(vlan_id)
61
62 if evc is None:
63 # Create EVC and initial EVC Map
64 evc = UtilityEVC(flow_entry)
65
66 # reapply the stag and name if forced vlan id
67 if use_default_vlan_id:
68 evc._s_tag = vlan_id
69 evc._name = evc._create_name(vlan_id)
70
71 evc_table[vlan_id] = evc
72 else:
73 if flow_entry.flow_id in evc.downstream_flows: # TODO: Debug only to see if flow_ids are unique
74 pass
75 else:
76 evc.add_downstream_flows(flow_entry.flow_id)
77
78 return evc
79
80 except Exception as e:
81 log.exception('utility-create', e=e)
82 return None
83
84 @property
85 def downstream_flows(self):
86 return frozenset(self._downstream_flows)
87
88 def add_downstream_flows(self, flow_id):
89 self._downstream_flows.add(flow_id)
90
91 def remove_downstream_flows(self, flow_id):
92 self._downstream_flows.discard(flow_id)
93
94 def remove(self, remove_maps=True):
95 """
96 Remove EVC (and optional associated EVC-MAPs) from hardware
97 :param remove_maps: (boolean)
98 :return: (deferred)
99 """
100 log.info('removing', evc=self, remove_maps=remove_maps)
101
102 device_id = self._flow.handler.device_id
103 flow_id = self._flow.flow_id
104 evc_table = _utility_evcs.get(device_id)
105
106 if evc_table is None:
107 return defer.succeed('NOP')
108
109 # Remove flow reference
110 if self._flow.flow_id in self._downstream_flows:
111 self._downstream_flows.discard(self._flow.flow_id)
112
113 if len(self._downstream_flows) == 0:
114 # Use base class to clean up
115 return super(UtilityEVC, self).remove(remove_maps=True)
116
117 return defer.succeed('More references')
118
119 @inlineCallbacks
120 def delete(self, delete_maps=True):
121 """
122 Remove from hardware and delete/clean-up EVC Object
123 :return: (deferred)
124 """
125 log.info('deleting', evc=self, delete_maps=delete_maps)
126
127 assert self._flow, 'Delete EVC must have flow reference'
128 try:
129 dl = [self.remove()]
130 if delete_maps:
131 for evc_map in self.evc_maps:
132 dl.append(evc_map.delete(None)) # TODO: implement bulk-flow procedures
133
134 yield defer.gatherResults(dl, consumeErrors=True)
135
136 self._evc_maps = None
137 f, self._flow = self._flow, None
138 if f is not None and f.handler is not None:
139 f.handler.remove_evc(self)
140
141 except Exception as e:
142 log.exception('removal', e=e)
143
144 returnValue('Done')
145
146 def reflow(self, reflow_maps=True):
147 pass # TODO: Implement or use base class?
148
149 @staticmethod
150 def remove_all(client, regex_=EVC_NAME_REGEX_ALL):
151 """
152 Remove all matching EVCs from hardware
153 :param client: (ncclient) NETCONF Client to use
154 :param regex_: (String) Regular expression for name matching
155 :return: (deferred)
156 """
157 _utility_evcs.clear()
158 EVC.remove_all(client, regex_)