Set lock_path correctly.
[openstack-build/neutron-build.git] / neutron / db / dvr_mac_db.py
1 # Copyright 2014 Hewlett-Packard Development Company, L.P.
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 from oslo_config import cfg
17 from oslo_db import exception as db_exc
18 from oslo_log import helpers as log_helpers
19 from oslo_log import log as logging
20 import sqlalchemy as sa
21 from sqlalchemy.orm import exc
22
23 from neutron._i18n import _, _LE
24 from neutron.common import exceptions as n_exc
25 from neutron.common import utils
26 from neutron.db import model_base
27 from neutron.extensions import dvr as ext_dvr
28 from neutron.extensions import portbindings
29 from neutron import manager
30
31
32 LOG = logging.getLogger(__name__)
33
34
35 dvr_mac_address_opts = [
36     cfg.StrOpt('dvr_base_mac',
37                default="fa:16:3f:00:00:00",
38                help=_("The base mac address used for unique "
39                       "DVR instances by Neutron. The first 3 octets will "
40                       "remain unchanged. If the 4th octet is not 00, it will "
41                       "also be used. The others will be randomly generated. "
42                       "The 'dvr_base_mac' *must* be different from "
43                       "'base_mac' to avoid mixing them up with MAC's "
44                       "allocated for tenant ports. A 4 octet example would be "
45                       "dvr_base_mac = fa:16:3f:4f:00:00. The default is 3 "
46                       "octet")),
47 ]
48 cfg.CONF.register_opts(dvr_mac_address_opts)
49
50
51 class DistributedVirtualRouterMacAddress(model_base.BASEV2):
52     """Represents a v2 neutron distributed virtual router mac address."""
53
54     __tablename__ = 'dvr_host_macs'
55
56     host = sa.Column(sa.String(255), primary_key=True, nullable=False)
57     mac_address = sa.Column(sa.String(32), nullable=False, unique=True)
58
59
60 class DVRDbMixin(ext_dvr.DVRMacAddressPluginBase):
61     """Mixin class to add dvr mac address to db_plugin_base_v2."""
62
63     @property
64     def plugin(self):
65         try:
66             if self._plugin is not None:
67                 return self._plugin
68         except AttributeError:
69             pass
70         self._plugin = manager.NeutronManager.get_plugin()
71         return self._plugin
72
73     def _get_dvr_mac_address_by_host(self, context, host):
74         try:
75             query = context.session.query(DistributedVirtualRouterMacAddress)
76             dvrma = query.filter(
77                 DistributedVirtualRouterMacAddress.host == host).one()
78         except exc.NoResultFound:
79             raise ext_dvr.DVRMacAddressNotFound(host=host)
80         return dvrma
81
82     def _create_dvr_mac_address(self, context, host):
83         """Create DVR mac address for a given host."""
84         base_mac = cfg.CONF.dvr_base_mac.split(':')
85         max_retries = cfg.CONF.mac_generation_retries
86         for attempt in reversed(range(max_retries)):
87             try:
88                 with context.session.begin(subtransactions=True):
89                     mac_address = utils.get_random_mac(base_mac)
90                     dvr_mac_binding = DistributedVirtualRouterMacAddress(
91                         host=host, mac_address=mac_address)
92                     context.session.add(dvr_mac_binding)
93                     LOG.debug("Generated DVR mac for host %(host)s "
94                               "is %(mac_address)s",
95                               {'host': host, 'mac_address': mac_address})
96                 dvr_macs = self.get_dvr_mac_address_list(context)
97                 # TODO(vivek): improve scalability of this fanout by
98                 # sending a single mac address rather than the entire set
99                 self.notifier.dvr_mac_address_update(context, dvr_macs)
100                 return self._make_dvr_mac_address_dict(dvr_mac_binding)
101             except db_exc.DBDuplicateEntry:
102                 LOG.debug("Generated DVR mac %(mac)s exists."
103                           " Remaining attempts %(attempts_left)s.",
104                           {'mac': mac_address, 'attempts_left': attempt})
105         LOG.error(_LE("MAC generation error after %s attempts"), max_retries)
106         raise ext_dvr.MacAddressGenerationFailure(host=host)
107
108     def get_dvr_mac_address_list(self, context):
109         with context.session.begin(subtransactions=True):
110             return (context.session.
111                     query(DistributedVirtualRouterMacAddress).all())
112
113     def get_dvr_mac_address_by_host(self, context, host):
114         """Determine the MAC for the DVR port associated to host."""
115         if not host:
116             return
117
118         try:
119             return self._get_dvr_mac_address_by_host(context, host)
120         except ext_dvr.DVRMacAddressNotFound:
121             return self._create_dvr_mac_address(context, host)
122
123     def _make_dvr_mac_address_dict(self, dvr_mac_entry, fields=None):
124         return {'host': dvr_mac_entry['host'],
125                 'mac_address': dvr_mac_entry['mac_address']}
126
127     @log_helpers.log_method_call
128     def get_ports_on_host_by_subnet(self, context, host, subnet):
129         """Returns DVR serviced ports on a given subnet in the input host
130
131         This method returns ports that need to be serviced by DVR.
132         :param context: rpc request context
133         :param host: host id to match and extract ports of interest
134         :param subnet: subnet id to match and extract ports of interest
135         :returns list -- Ports on the given subnet in the input host
136         """
137         # FIXME(vivek, salv-orlando): improve this query by adding the
138         # capability of filtering by binding:host_id
139         ports_by_host = []
140         filter = {'fixed_ips': {'subnet_id': [subnet]}}
141         ports = self.plugin.get_ports(context, filters=filter)
142         LOG.debug("List of Ports on subnet %(subnet)s at host %(host)s "
143                   "received as %(ports)s",
144                   {'subnet': subnet, 'host': host, 'ports': ports})
145         for port in ports:
146             device_owner = port['device_owner']
147             if (utils.is_dvr_serviced(device_owner)):
148                 if port[portbindings.HOST_ID] == host:
149                     port_dict = self.plugin._make_port_dict(port,
150                         process_extensions=False)
151                     ports_by_host.append(port_dict)
152         LOG.debug("Returning list of dvr serviced ports on host %(host)s"
153                   " for subnet %(subnet)s ports %(ports)s",
154                   {'host': host, 'subnet': subnet,
155                    'ports': ports_by_host})
156         return ports_by_host
157
158     @log_helpers.log_method_call
159     def get_subnet_for_dvr(self, context, subnet, fixed_ips=None):
160         if fixed_ips:
161             subnet_data = fixed_ips[0]['subnet_id']
162         else:
163             subnet_data = subnet
164         try:
165             subnet_info = self.plugin.get_subnet(
166                 context, subnet_data)
167         except n_exc.SubnetNotFound:
168             return {}
169         else:
170             # retrieve the gateway port on this subnet
171             if fixed_ips:
172                 ip_address = fixed_ips[0]['ip_address']
173             else:
174                 ip_address = subnet_info['gateway_ip']
175
176             filter = {'fixed_ips': {'subnet_id': [subnet],
177                                     'ip_address': [ip_address]}}
178
179             internal_gateway_ports = self.plugin.get_ports(
180                 context, filters=filter)
181             if not internal_gateway_ports:
182                 LOG.error(_LE("Could not retrieve gateway port "
183                               "for subnet %s"), subnet_info)
184                 return {}
185             internal_port = internal_gateway_ports[0]
186             subnet_info['gateway_mac'] = internal_port['mac_address']
187             return subnet_info