Set lock_path correctly.
[openstack-build/neutron-build.git] / neutron / api / rpc / handlers / dhcp_rpc.py
1 # Copyright (c) 2012 OpenStack Foundation.
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
12 # implied.
13 # See the License for the specific language governing permissions and
14 # limitations under the License.
15
16 import copy
17 import itertools
18 import operator
19
20 from oslo_config import cfg
21 from oslo_db import exception as db_exc
22 from oslo_log import log as logging
23 import oslo_messaging
24 from oslo_utils import excutils
25
26 from neutron._i18n import _, _LW
27 from neutron.api.v2 import attributes
28 from neutron.common import constants
29 from neutron.common import exceptions as n_exc
30 from neutron.common import utils
31 from neutron.db import api as db_api
32 from neutron.extensions import portbindings
33 from neutron import manager
34 from neutron.plugins.common import utils as p_utils
35 from neutron.quota import resource_registry
36
37
38 LOG = logging.getLogger(__name__)
39
40
41 class DhcpRpcCallback(object):
42     """DHCP agent RPC callback in plugin implementations.
43
44     This class implements the server side of an rpc interface.  The client
45     side of this interface can be found in
46     neutron.agent.dhcp.agent.DhcpPluginApi.  For more information about
47     changing rpc interfaces, see doc/source/devref/rpc_api.rst.
48     """
49
50     # API version history:
51     #     1.0 - Initial version.
52     #     1.1 - Added get_active_networks_info, create_dhcp_port,
53     #           and update_dhcp_port methods.
54     #     1.2 - Removed get_dhcp_port. When removing a method (Making a
55     #           backwards incompatible change) you would normally bump the
56     #           major version. However, since the method was unused in the
57     #           RPC client for many releases, it should be OK to bump the
58     #           minor release instead and claim RPC compatibility with the
59     #           last few client versions.
60     #     1.3 - Removed release_port_fixed_ip. It's not used by reference DHCP
61     #           agent since Juno, so similar rationale for not bumping the
62     #           major version as above applies here too.
63     target = oslo_messaging.Target(
64         namespace=constants.RPC_NAMESPACE_DHCP_PLUGIN,
65         version='1.3')
66
67     def _get_active_networks(self, context, **kwargs):
68         """Retrieve and return a list of the active networks."""
69         host = kwargs.get('host')
70         plugin = manager.NeutronManager.get_plugin()
71         if utils.is_extension_supported(
72             plugin, constants.DHCP_AGENT_SCHEDULER_EXT_ALIAS):
73             if cfg.CONF.network_auto_schedule:
74                 plugin.auto_schedule_networks(context, host)
75             nets = plugin.list_active_networks_on_active_dhcp_agent(
76                 context, host)
77         else:
78             filters = dict(admin_state_up=[True])
79             nets = plugin.get_networks(context, filters=filters)
80         return nets
81
82     def _port_action(self, plugin, context, port, action):
83         """Perform port operations taking care of concurrency issues."""
84         try:
85             if action == 'create_port':
86                 return p_utils.create_port(plugin, context, port)
87             elif action == 'update_port':
88                 return plugin.update_port(context, port['id'], port)
89             else:
90                 msg = _('Unrecognized action')
91                 raise n_exc.Invalid(message=msg)
92         except (db_exc.DBError, n_exc.NetworkNotFound,
93                 n_exc.SubnetNotFound, n_exc.IpAddressGenerationFailure) as e:
94             with excutils.save_and_reraise_exception(reraise=False) as ctxt:
95                 if isinstance(e, n_exc.IpAddressGenerationFailure):
96                     # Check if the subnet still exists and if it does not,
97                     # this is the reason why the ip address generation failed.
98                     # In any other unlikely event re-raise
99                     try:
100                         subnet_id = port['port']['fixed_ips'][0]['subnet_id']
101                         plugin.get_subnet(context, subnet_id)
102                     except n_exc.SubnetNotFound:
103                         pass
104                     else:
105                         ctxt.reraise = True
106                 net_id = port['port']['network_id']
107                 LOG.warn(_LW("Action %(action)s for network %(net_id)s "
108                              "could not complete successfully: %(reason)s"),
109                          {"action": action, "net_id": net_id, 'reason': e})
110
111     def get_active_networks(self, context, **kwargs):
112         """Retrieve and return a list of the active network ids."""
113         # NOTE(arosen): This method is no longer used by the DHCP agent but is
114         # left so that neutron-dhcp-agents will still continue to work if
115         # neutron-server is upgraded and not the agent.
116         host = kwargs.get('host')
117         LOG.debug('get_active_networks requested from %s', host)
118         nets = self._get_active_networks(context, **kwargs)
119         return [net['id'] for net in nets]
120
121     def _group_by_network_id(self, res):
122         grouped = {}
123         keyfunc = operator.itemgetter('network_id')
124         for net_id, values in itertools.groupby(sorted(res, key=keyfunc),
125                                                 keyfunc):
126             grouped[net_id] = list(values)
127         return grouped
128
129     def get_active_networks_info(self, context, **kwargs):
130         """Returns all the networks/subnets/ports in system."""
131         host = kwargs.get('host')
132         LOG.debug('get_active_networks_info from %s', host)
133         networks = self._get_active_networks(context, **kwargs)
134         plugin = manager.NeutronManager.get_plugin()
135         filters = {'network_id': [network['id'] for network in networks]}
136         ports = plugin.get_ports(context, filters=filters)
137         filters['enable_dhcp'] = [True]
138         subnets = plugin.get_subnets(context, filters=filters)
139
140         grouped_subnets = self._group_by_network_id(subnets)
141         grouped_ports = self._group_by_network_id(ports)
142         for network in networks:
143             network['subnets'] = grouped_subnets.get(network['id'], [])
144             network['ports'] = grouped_ports.get(network['id'], [])
145
146         return networks
147
148     def get_network_info(self, context, **kwargs):
149         """Retrieve and return extended information about a network."""
150         network_id = kwargs.get('network_id')
151         host = kwargs.get('host')
152         LOG.debug('Network %(network_id)s requested from '
153                   '%(host)s', {'network_id': network_id,
154                                'host': host})
155         plugin = manager.NeutronManager.get_plugin()
156         try:
157             network = plugin.get_network(context, network_id)
158         except n_exc.NetworkNotFound:
159             LOG.warn(_LW("Network %s could not be found, it might have "
160                          "been deleted concurrently."), network_id)
161             return
162         filters = dict(network_id=[network_id])
163         network['subnets'] = plugin.get_subnets(context, filters=filters)
164         network['ports'] = plugin.get_ports(context, filters=filters)
165         return network
166
167     @db_api.retry_db_errors
168     def release_dhcp_port(self, context, **kwargs):
169         """Release the port currently being used by a DHCP agent."""
170         host = kwargs.get('host')
171         network_id = kwargs.get('network_id')
172         device_id = kwargs.get('device_id')
173
174         LOG.debug('DHCP port deletion for %(network_id)s request from '
175                   '%(host)s',
176                   {'network_id': network_id, 'host': host})
177         plugin = manager.NeutronManager.get_plugin()
178         plugin.delete_ports_by_device_id(context, device_id, network_id)
179
180     def update_lease_expiration(self, context, **kwargs):
181         """Release the fixed_ip associated the subnet on a port."""
182         # NOTE(arosen): This method is no longer used by the DHCP agent but is
183         # left so that neutron-dhcp-agents will still continue to work if
184         # neutron-server is upgraded and not the agent.
185         host = kwargs.get('host')
186
187         LOG.warning(_LW('Updating lease expiration is now deprecated. Issued  '
188                         'from host %s.'), host)
189
190     @db_api.retry_db_errors
191     @resource_registry.mark_resources_dirty
192     def create_dhcp_port(self, context, **kwargs):
193         """Create and return dhcp port information.
194
195         If an expected failure occurs, a None port is returned.
196
197         """
198         host = kwargs.get('host')
199         # Note(pbondar): Create deep copy of port to prevent operating
200         # on changed dict if RetryRequest is raised
201         port = copy.deepcopy(kwargs.get('port'))
202         LOG.debug('Create dhcp port %(port)s '
203                   'from %(host)s.',
204                   {'port': port,
205                    'host': host})
206
207         port['port']['device_owner'] = constants.DEVICE_OWNER_DHCP
208         port['port'][portbindings.HOST_ID] = host
209         if 'mac_address' not in port['port']:
210             port['port']['mac_address'] = attributes.ATTR_NOT_SPECIFIED
211         plugin = manager.NeutronManager.get_plugin()
212         return self._port_action(plugin, context, port, 'create_port')
213
214     @db_api.retry_db_errors
215     def update_dhcp_port(self, context, **kwargs):
216         """Update the dhcp port."""
217         host = kwargs.get('host')
218         port = kwargs.get('port')
219         port['id'] = kwargs.get('port_id')
220         port['port'][portbindings.HOST_ID] = host
221         plugin = manager.NeutronManager.get_plugin()
222         old_port = plugin.get_port(context, port['id'])
223         if (old_port['device_id'] != constants.DEVICE_ID_RESERVED_DHCP_PORT
224             and old_port['device_id'] !=
225             utils.get_dhcp_agent_device_id(port['port']['network_id'], host)):
226             raise n_exc.DhcpPortInUse(port_id=port['id'])
227         LOG.debug('Update dhcp port %(port)s '
228                   'from %(host)s.',
229                   {'port': port,
230                    'host': host})
231         return self._port_action(plugin, context, port, 'update_port')