1 # Copyright (c) 2015 OpenStack Foundation.
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
8 # http://www.apache.org/licenses/LICENSE-2.0
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
18 from oslo_config import cfg
19 from oslo_log import log as logging
20 from sqlalchemy.orm import exc
22 from neutron.api.v2 import attributes
23 from neutron.common import constants
24 from neutron.common import exceptions as n_exc
25 from neutron.common import utils
26 from neutron.db import common_db_mixin
27 from neutron.db import models_v2
29 LOG = logging.getLogger(__name__)
32 def convert_result_to_dict(f):
34 def inner(*args, **kwargs):
35 result = f(*args, **kwargs)
39 elif isinstance(result, list):
40 return [r.to_dict() for r in result]
42 return result.to_dict()
48 def inner_filter(*args, **kwargs):
49 result = f(*args, **kwargs)
50 fields = kwargs.get('fields')
53 pos = f.__code__.co_varnames.index('fields')
55 except (IndexError, ValueError):
58 do_filter = lambda d: {k: v for k, v in d.items() if k in fields}
59 if isinstance(result, list):
60 return [do_filter(obj) for obj in result]
62 return do_filter(result)
66 class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
67 """Stores getters and helper methods for db_base_plugin_v2
69 All private getters and simple helpers like _make_*_dict were moved from
71 More complicated logic and public methods left in db_base_plugin_v2.
72 Main purpose of this class is to make getters accessible for Ipam
78 return utils.get_random_mac(cfg.CONF.base_mac.split(':'))
81 def _delete_ip_allocation(context, network_id, subnet_id, ip_address):
83 # Delete the IP address from the IPAllocate table
84 LOG.debug("Delete allocated IP %(ip_address)s "
85 "(%(network_id)s/%(subnet_id)s)",
86 {'ip_address': ip_address,
87 'network_id': network_id,
88 'subnet_id': subnet_id})
89 context.session.query(models_v2.IPAllocation).filter_by(
90 network_id=network_id,
91 ip_address=ip_address,
92 subnet_id=subnet_id).delete()
95 def _store_ip_allocation(context, ip_address, network_id, subnet_id,
97 LOG.debug("Allocated IP %(ip_address)s "
98 "(%(network_id)s/%(subnet_id)s/%(port_id)s)",
99 {'ip_address': ip_address,
100 'network_id': network_id,
101 'subnet_id': subnet_id,
103 allocated = models_v2.IPAllocation(
104 network_id=network_id,
106 ip_address=ip_address,
109 context.session.add(allocated)
111 def _make_subnet_dict(self, subnet, fields=None, context=None):
112 res = {'id': subnet['id'],
113 'name': subnet['name'],
114 'tenant_id': subnet['tenant_id'],
115 'network_id': subnet['network_id'],
116 'ip_version': subnet['ip_version'],
117 'cidr': subnet['cidr'],
118 'subnetpool_id': subnet.get('subnetpool_id'),
119 'allocation_pools': [{'start': pool['first_ip'],
120 'end': pool['last_ip']}
121 for pool in subnet['allocation_pools']],
122 'gateway_ip': subnet['gateway_ip'],
123 'enable_dhcp': subnet['enable_dhcp'],
124 'ipv6_ra_mode': subnet['ipv6_ra_mode'],
125 'ipv6_address_mode': subnet['ipv6_address_mode'],
126 'dns_nameservers': [dns['address']
127 for dns in subnet['dns_nameservers']],
128 'host_routes': [{'destination': route['destination'],
129 'nexthop': route['nexthop']}
130 for route in subnet['routes']],
132 # The shared attribute for a subnet is the same as its parent network
133 res['shared'] = self._is_network_shared(context, subnet.networks)
134 # Call auxiliary extend functions, if any
135 self._apply_dict_extend_functions(attributes.SUBNETS, res, subnet)
136 return self._fields(res, fields)
138 def _make_subnetpool_dict(self, subnetpool, fields=None):
139 default_prefixlen = str(subnetpool['default_prefixlen'])
140 min_prefixlen = str(subnetpool['min_prefixlen'])
141 max_prefixlen = str(subnetpool['max_prefixlen'])
142 res = {'id': subnetpool['id'],
143 'name': subnetpool['name'],
144 'tenant_id': subnetpool['tenant_id'],
145 'default_prefixlen': default_prefixlen,
146 'min_prefixlen': min_prefixlen,
147 'max_prefixlen': max_prefixlen,
148 'is_default': subnetpool['is_default'],
149 'shared': subnetpool['shared'],
150 'prefixes': [prefix['cidr']
151 for prefix in subnetpool['prefixes']],
152 'ip_version': subnetpool['ip_version'],
153 'default_quota': subnetpool['default_quota'],
154 'address_scope_id': subnetpool['address_scope_id']}
155 return self._fields(res, fields)
157 def _make_port_dict(self, port, fields=None,
158 process_extensions=True):
159 res = {"id": port["id"],
160 'name': port['name'],
161 "network_id": port["network_id"],
162 'tenant_id': port['tenant_id'],
163 "mac_address": port["mac_address"],
164 "admin_state_up": port["admin_state_up"],
165 "status": port["status"],
166 "fixed_ips": [{'subnet_id': ip["subnet_id"],
167 'ip_address': ip["ip_address"]}
168 for ip in port["fixed_ips"]],
169 "device_id": port["device_id"],
170 "device_owner": port["device_owner"]}
171 if "dns_name" in port:
172 res["dns_name"] = port["dns_name"]
173 if "dns_assignment" in port:
174 res["dns_assignment"] = [{"ip_address": a["ip_address"],
175 "hostname": a["hostname"],
177 for a in port["dns_assignment"]]
178 # Call auxiliary extend functions, if any
179 if process_extensions:
180 self._apply_dict_extend_functions(
181 attributes.PORTS, res, port)
182 return self._fields(res, fields)
184 def _get_network(self, context, id):
186 network = self._get_by_id(context, models_v2.Network, id)
187 except exc.NoResultFound:
188 raise n_exc.NetworkNotFound(net_id=id)
191 def _get_subnet(self, context, id):
193 subnet = self._get_by_id(context, models_v2.Subnet, id)
194 except exc.NoResultFound:
195 raise n_exc.SubnetNotFound(subnet_id=id)
198 def _get_subnetpool(self, context, id):
200 return self._get_by_id(context, models_v2.SubnetPool, id)
201 except exc.NoResultFound:
202 raise n_exc.SubnetPoolNotFound(subnetpool_id=id)
204 def _get_all_subnetpools(self, context):
205 # NOTE(tidwellr): see note in _get_all_subnets()
206 return context.session.query(models_v2.SubnetPool).all()
208 def _get_subnetpools_by_address_scope_id(self, context, address_scope_id):
209 # NOTE(vikram.choudhary): see note in _get_all_subnets()
210 subnetpool_qry = context.session.query(models_v2.SubnetPool)
211 return subnetpool_qry.filter_by(
212 address_scope_id=address_scope_id).all()
214 def _get_port(self, context, id):
216 port = self._get_by_id(context, models_v2.Port, id)
217 except exc.NoResultFound:
218 raise n_exc.PortNotFound(port_id=id)
221 def _get_dns_by_subnet(self, context, subnet_id):
222 dns_qry = context.session.query(models_v2.DNSNameServer)
223 return dns_qry.filter_by(subnet_id=subnet_id).order_by(
224 models_v2.DNSNameServer.order).all()
226 def _get_route_by_subnet(self, context, subnet_id):
227 route_qry = context.session.query(models_v2.SubnetRoute)
228 return route_qry.filter_by(subnet_id=subnet_id).all()
230 def _get_router_gw_ports_by_network(self, context, network_id):
231 port_qry = context.session.query(models_v2.Port)
232 return port_qry.filter_by(network_id=network_id,
233 device_owner=constants.DEVICE_OWNER_ROUTER_GW).all()
235 def _get_subnets_by_network(self, context, network_id):
236 subnet_qry = context.session.query(models_v2.Subnet)
237 return subnet_qry.filter_by(network_id=network_id).all()
239 def _get_subnets_by_subnetpool(self, context, subnetpool_id):
240 subnet_qry = context.session.query(models_v2.Subnet)
241 return subnet_qry.filter_by(subnetpool_id=subnetpool_id).all()
243 def _get_all_subnets(self, context):
244 # NOTE(salvatore-orlando): This query might end up putting
245 # a lot of stress on the db. Consider adding a cache layer
246 return context.session.query(models_v2.Subnet).all()
248 def _get_subnets(self, context, filters=None, fields=None,
249 sorts=None, limit=None, marker=None,
251 marker_obj = self._get_marker_obj(context, 'subnet', limit, marker)
252 make_subnet_dict = functools.partial(self._make_subnet_dict,
254 return self._get_collection(context, models_v2.Subnet,
256 filters=filters, fields=fields,
259 marker_obj=marker_obj,
260 page_reverse=page_reverse)
262 def _make_network_dict(self, network, fields=None,
263 process_extensions=True, context=None):
264 res = {'id': network['id'],
265 'name': network['name'],
266 'tenant_id': network['tenant_id'],
267 'admin_state_up': network['admin_state_up'],
268 'mtu': network.get('mtu', constants.DEFAULT_NETWORK_MTU),
269 'status': network['status'],
270 'subnets': [subnet['id']
271 for subnet in network['subnets']]}
272 res['shared'] = self._is_network_shared(context, network)
273 # Call auxiliary extend functions, if any
274 if process_extensions:
275 self._apply_dict_extend_functions(
276 attributes.NETWORKS, res, network)
277 return self._fields(res, fields)
279 def _is_network_shared(self, context, network):
280 # The shared attribute for a network now reflects if the network
281 # is shared to the calling tenant via an RBAC entry.
282 matches = ('*',) + ((context.tenant_id,) if context else ())
283 for entry in network.rbac_entries:
284 if (entry.action == 'access_as_shared' and
285 entry.target_tenant in matches):
289 def _make_subnet_args(self, detail, subnet, subnetpool_id):
290 gateway_ip = str(detail.gateway_ip) if detail.gateway_ip else None
291 args = {'tenant_id': detail.tenant_id,
292 'id': detail.subnet_id,
293 'name': subnet['name'],
294 'network_id': subnet['network_id'],
295 'ip_version': subnet['ip_version'],
296 'cidr': str(detail.subnet_cidr),
297 'subnetpool_id': subnetpool_id,
298 'enable_dhcp': subnet['enable_dhcp'],
299 'gateway_ip': gateway_ip}
300 if subnet['ip_version'] == 6 and subnet['enable_dhcp']:
301 if attributes.is_attr_set(subnet['ipv6_ra_mode']):
302 args['ipv6_ra_mode'] = subnet['ipv6_ra_mode']
303 if attributes.is_attr_set(subnet['ipv6_address_mode']):
304 args['ipv6_address_mode'] = subnet['ipv6_address_mode']
307 def _make_fixed_ip_dict(self, ips):
308 # Excludes from dict all keys except subnet_id and ip_address
309 return [{'subnet_id': ip["subnet_id"],
310 'ip_address': ip["ip_address"]}