908842b1bc5a77331c9014d0ae02b2e3b1d269be
[openstack-build/neutron-build.git] / neutron / plugins / ml2 / drivers / l2pop / rpc_manager / l2population_rpc.py
1 # Copyright (c) 2013 OpenStack Foundation.
2 # All Rights Reserved.
3 #
4 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
5 #    not use this file except in compliance with the License. You may obtain
6 #    a copy of the License at
7 #
8 #         http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #    Unless required by applicable law or agreed to in writing, software
11 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 #    License for the specific language governing permissions and limitations
14 #    under the License.
15
16 import abc
17
18 from oslo_config import cfg
19 from oslo_log import helpers as log_helpers
20 import six
21
22 from neutron.common import constants as n_const
23 from neutron.plugins.ml2.drivers.l2pop import rpc as l2pop_rpc
24
25
26 @six.add_metaclass(abc.ABCMeta)
27 class L2populationRpcCallBackMixin(object):
28     '''General mixin class of L2-population RPC call back.
29
30     The following methods are called through RPC.
31         add_fdb_entries(), remove_fdb_entries(), update_fdb_entries()
32     The following methods are used in an agent as internal methods.
33         fdb_add(), fdb_remove(), fdb_update()
34     '''
35
36     @log_helpers.log_method_call
37     def add_fdb_entries(self, context, fdb_entries, host=None):
38         if not host or host == cfg.CONF.host:
39             self.fdb_add(context, self._unmarshall_fdb_entries(fdb_entries))
40
41     @log_helpers.log_method_call
42     def remove_fdb_entries(self, context, fdb_entries, host=None):
43         if not host or host == cfg.CONF.host:
44             self.fdb_remove(context, self._unmarshall_fdb_entries(fdb_entries))
45
46     @log_helpers.log_method_call
47     def update_fdb_entries(self, context, fdb_entries, host=None):
48         if not host or host == cfg.CONF.host:
49             self.fdb_update(context, self._unmarshall_fdb_entries(fdb_entries))
50
51     @staticmethod
52     def _unmarshall_fdb_entries(fdb_entries):
53         """Prepares fdb_entries from JSON.
54
55         All methods in this class that receive messages should call this to
56         unmarshall fdb_entries from the wire.
57
58         :param fdb_entries: Original fdb_entries data-structure.  Looks like:
59             {
60                 <uuid>: {
61                     ...,
62                     'ports': {
63                         <ip address>: [ [<mac>, <ip>], ...  ],
64                         ...
65
66         :returns: Deep copy with [<mac>, <ip>] converted to PortInfo
67         """
68         unmarshalled = dict(fdb_entries)
69         for value in unmarshalled.values():
70             if 'ports' in value:
71                 value['ports'] = dict(
72                     (address, [l2pop_rpc.PortInfo(*pi) for pi in port_infos])
73                     for address, port_infos in value['ports'].items()
74                 )
75         return unmarshalled
76
77     @abc.abstractmethod
78     def fdb_add(self, context, fdb_entries):
79         pass
80
81     @abc.abstractmethod
82     def fdb_remove(self, context, fdb_entries):
83         pass
84
85     @abc.abstractmethod
86     def fdb_update(self, context, fdb_entries):
87         pass
88
89
90 class L2populationRpcCallBackTunnelMixin(L2populationRpcCallBackMixin):
91     '''Mixin class of L2-population call back for Tunnel.
92
93     The following methods are all used in agents as internal methods.
94
95     Some of the methods in this class use Local VLAN Mapping, aka lvm.
96     It's a python object with at least the following attributes:
97
98     ============ =========================================================
99     Attribute    Description
100     ============ =========================================================
101     vlan         An identifier used by the agent to identify a neutron
102                  network.
103     network_type A network type found in neutron.plugins.common.constants.
104     ============ =========================================================
105
106     NOTE(yamamoto): "Local VLAN" is an OVS-agent term.  OVS-agent internally
107     uses 802.1q VLAN tagging to isolate networks.  While this class inherited
108     the terms from OVS-agent, it does not assume the specific underlying
109     technologies.  E.g. this class is also used by ofagent, where a different
110     mechanism is used.
111     '''
112
113     @abc.abstractmethod
114     def add_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
115         '''Add flow for fdb
116
117         This method is assumed to be used by method fdb_add_tun.
118         We expect to add a flow entry to send a packet to specified port
119         on bridge.
120         And you may edit some information for local arp response.
121
122         :param br: represent the bridge on which add_fdb_flow should be
123         applied.
124         :param port_info: PortInfo instance to include mac and ip.
125             .mac_address
126             .ip_address
127
128         :remote_ip: remote ip address.
129         :param lvm: a local VLAN map of network.
130         :param ofport: a port to add.
131         '''
132         pass
133
134     @abc.abstractmethod
135     def del_fdb_flow(self, br, port_info, remote_ip, lvm, ofport):
136         '''Delete flow for fdb
137
138         This method is assumed to be used by method fdb_remove_tun.
139         We expect to delete a flow entry to send a packet to specified port
140         from bridge.
141         And you may delete some information for local arp response.
142
143         :param br: represent the bridge on which del_fdb_flow should be
144         applied.
145         :param port_info: PortInfo instance to include mac and ip.
146             .mac_address
147             .ip_address
148
149         :remote_ip: remote ip address.
150         :param lvm: local VLAN map of a network. See add_fdb_flow for
151             more explanation.
152         :param ofport: a port to delete.
153         '''
154         pass
155
156     @abc.abstractmethod
157     def setup_tunnel_port(self, br, remote_ip, network_type):
158         '''Setup an added tunnel port.
159
160         This method is assumed to be used by method fdb_add_tun.
161         We expect to prepare to call add_fdb_flow. It will be mainly adding
162         a port to a bridge.
163         If you need, you may do some preparations for a bridge.
164
165         :param br: represent the bridge on which setup_tunnel_port should be
166         applied.
167         :param remote_ip: an ip for a port to setup.
168         :param network_type: a type of a network.
169         :returns: an ofport value. value 0 means the port is unavailable.
170         '''
171         pass
172
173     @abc.abstractmethod
174     def cleanup_tunnel_port(self, br, tun_ofport, tunnel_type):
175         '''Clean up a deleted tunnel port.
176
177         This method is assumed to be used by method fdb_remove_tun.
178         We expect to clean up after calling del_fdb_flow. It will be mainly
179         deleting a port from a bridge.
180         If you need, you may do some cleanup for a bridge.
181
182         :param br: represent the bridge on which cleanup_tunnel_port should be
183         applied.
184         :param tun_ofport: a port value to cleanup.
185         :param tunnel_type: a type of a tunnel.
186         '''
187         pass
188
189     @abc.abstractmethod
190     def setup_entry_for_arp_reply(self, br, action, local_vid, mac_address,
191                                   ip_address):
192         '''Operate the ARP respond information.
193
194         Update MAC/IPv4 associations, which is typically used by
195         the local ARP responder.  For example, OVS-agent sets up
196         flow entries to perform ARP responses.
197
198         :param br: represent the bridge on which setup_entry_for_arp_reply
199         should be applied.
200         :param action: add/remove flow for arp response information.
201         :param local_vid: id in local VLAN map of network's ARP entry.
202         :param mac_address: MAC string value.
203         :param ip_address: IP string value.
204         '''
205         pass
206
207     def get_agent_ports(self, fdb_entries, local_vlan_map):
208         """Generator to yield port info.
209
210         For each known (i.e found in local_vlan_map) network in
211         fdb_entries, yield (lvm, fdb_entries[network_id]['ports']) pair.
212
213         :param fdb_entries: l2pop fdb entries
214         :param local_vlan_map: A dict to map network_id to
215             the corresponding lvm entry.
216         """
217         for network_id, values in fdb_entries.items():
218             lvm = local_vlan_map.get(network_id)
219             if lvm is None:
220                 continue
221             agent_ports = values.get('ports')
222             yield (lvm, agent_ports)
223
224     @log_helpers.log_method_call
225     def fdb_add_tun(self, context, br, lvm, agent_ports, lookup_port):
226         for remote_ip, ports in agent_ports.items():
227             # Ensure we have a tunnel port with this remote agent
228             ofport = lookup_port(lvm.network_type, remote_ip)
229             if not ofport:
230                 ofport = self.setup_tunnel_port(br, remote_ip,
231                                                 lvm.network_type)
232                 if ofport == 0:
233                     continue
234             for port in ports:
235                 self.add_fdb_flow(br, port, remote_ip, lvm, ofport)
236
237     @log_helpers.log_method_call
238     def fdb_remove_tun(self, context, br, lvm, agent_ports, lookup_port):
239         for remote_ip, ports in agent_ports.items():
240             ofport = lookup_port(lvm.network_type, remote_ip)
241             if not ofport:
242                 continue
243             for port in ports:
244                 self.del_fdb_flow(br, port, remote_ip, lvm, ofport)
245                 if port == n_const.FLOODING_ENTRY:
246                     # Check if this tunnel port is still used
247                     self.cleanup_tunnel_port(br, ofport, lvm.network_type)
248
249     @log_helpers.log_method_call
250     def fdb_update(self, context, fdb_entries):
251         '''Call methods named '_fdb_<action>'.
252
253         This method assumes that methods '_fdb_<action>' are defined in class.
254         Currently the following actions are available.
255             chg_ip
256         '''
257         for action, values in fdb_entries.items():
258             method = '_fdb_' + action
259             if not hasattr(self, method):
260                 raise NotImplementedError()
261
262             getattr(self, method)(context, values)
263
264     @log_helpers.log_method_call
265     def fdb_chg_ip_tun(self, context, br, fdb_entries, local_ip,
266                        local_vlan_map):
267         '''fdb update when an IP of a port is updated.
268
269         The ML2 l2-pop mechanism driver sends an fdb update rpc message when an
270         IP of a port is updated.
271
272         :param context: RPC context.
273         :param br: represent the bridge on which fdb_chg_ip_tun should be
274         applied.
275         :param fdb_entries: fdb dicts that contain all mac/IP information per
276                             agent and network.
277                                {'net1':
278                                 {'agent_ip':
279                                  {'before': PortInfo,
280                                   'after': PortInfo
281                                  }
282                                 }
283                                 'net2':
284                                 ...
285                                }
286
287                              PortInfo has .mac_address and .ip_address attrs.
288
289         :param local_ip: local IP address of this agent.
290         :param local_vlan_map: A dict to map network_id to
291             the corresponding lvm entry.
292         '''
293
294         for network_id, agent_ports in fdb_entries.items():
295             lvm = local_vlan_map.get(network_id)
296             if not lvm:
297                 continue
298
299             for agent_ip, state in agent_ports.items():
300                 if agent_ip == local_ip:
301                     continue
302
303                 after = state.get('after', [])
304                 for mac_ip in after:
305                     self.setup_entry_for_arp_reply(br, 'add', lvm.vlan,
306                                                    mac_ip.mac_address,
307                                                    mac_ip.ip_address)
308
309                 before = state.get('before', [])
310                 for mac_ip in before:
311                     self.setup_entry_for_arp_reply(br, 'remove', lvm.vlan,
312                                                    mac_ip.mac_address,
313                                                    mac_ip.ip_address)