"Fossies" - the Fresh Open Source Software Archive

Member "neutron-14.0.3/neutron/db/db_base_plugin_common.py" (22 Oct 2019, 15127 Bytes) of package /linux/misc/openstack/neutron-14.0.3.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "db_base_plugin_common.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.0.2_vs_14.0.3.

    1 # Copyright (c) 2015 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 functools
   17 
   18 import netaddr
   19 import six
   20 
   21 from neutron_lib.api.definitions import network as net_def
   22 from neutron_lib.api.definitions import port as port_def
   23 from neutron_lib.api.definitions import subnet as subnet_def
   24 from neutron_lib.api.definitions import subnetpool as subnetpool_def
   25 from neutron_lib.api import validators
   26 from neutron_lib import constants
   27 from neutron_lib.db import api as db_api
   28 from neutron_lib.db import model_query
   29 from neutron_lib.db import resource_extend
   30 from neutron_lib.db import utils as db_utils
   31 from neutron_lib import exceptions
   32 from neutron_lib.utils import net
   33 from oslo_config import cfg
   34 from oslo_log import log as logging
   35 from sqlalchemy.orm import exc
   36 
   37 from neutron.common import constants as n_const
   38 from neutron.db import common_db_mixin
   39 from neutron.db import models_v2
   40 from neutron.objects import base as base_obj
   41 from neutron.objects import ports as port_obj
   42 from neutron.objects import subnet as subnet_obj
   43 from neutron.objects import subnetpool as subnetpool_obj
   44 
   45 LOG = logging.getLogger(__name__)
   46 
   47 
   48 def convert_result_to_dict(f):
   49     @functools.wraps(f)
   50     def inner(*args, **kwargs):
   51         result = f(*args, **kwargs)
   52 
   53         if result is None:
   54             return None
   55         elif isinstance(result, list):
   56             return [r.to_dict() for r in result]
   57         else:
   58             return result.to_dict()
   59     return inner
   60 
   61 
   62 def filter_fields(f):
   63     @functools.wraps(f)
   64     def inner_filter(*args, **kwargs):
   65         result = f(*args, **kwargs)
   66         fields = kwargs.get('fields')
   67         if not fields:
   68             try:
   69                 pos = f.__code__.co_varnames.index('fields')
   70                 fields = args[pos]
   71             except (IndexError, ValueError):
   72                 return result
   73 
   74         do_filter = lambda d: {k: v for k, v in d.items() if k in fields}
   75         if isinstance(result, list):
   76             return [do_filter(obj) for obj in result]
   77         else:
   78             return do_filter(result)
   79     return inner_filter
   80 
   81 
   82 class DbBasePluginCommon(common_db_mixin.CommonDbMixin):
   83     """Stores getters and helper methods for db_base_plugin_v2
   84 
   85     All private getters and simple helpers like _make_*_dict were moved from
   86     db_base_plugin_v2.
   87     More complicated logic and public methods left in db_base_plugin_v2.
   88     Main purpose of this class is to make getters accessible for Ipam
   89     backends.
   90     """
   91 
   92     @staticmethod
   93     def _generate_macs(mac_count=1):
   94         mac_maker = net.random_mac_generator(cfg.CONF.base_mac.split(':'))
   95         return [six.next(mac_maker) for x in range(mac_count)]
   96 
   97     @db_api.CONTEXT_READER
   98     def _is_mac_in_use(self, context, network_id, mac_address):
   99         return port_obj.Port.objects_exist(context, network_id=network_id,
  100                                            mac_address=mac_address)
  101 
  102     @staticmethod
  103     def _delete_ip_allocation(context, network_id, subnet_id, ip_address):
  104 
  105         # Delete the IP address from the IPAllocate table
  106         LOG.debug("Delete allocated IP %(ip_address)s "
  107                   "(%(network_id)s/%(subnet_id)s)",
  108                   {'ip_address': ip_address,
  109                    'network_id': network_id,
  110                    'subnet_id': subnet_id})
  111         port_obj.IPAllocation.delete_objects(
  112             context, network_id=network_id, ip_address=ip_address,
  113             subnet_id=subnet_id)
  114 
  115     @staticmethod
  116     @db_api.CONTEXT_WRITER
  117     def _store_ip_allocation(context, ip_address, network_id, subnet_id,
  118                              port_id):
  119         LOG.debug("Allocated IP %(ip_address)s "
  120                   "(%(network_id)s/%(subnet_id)s/%(port_id)s)",
  121                   {'ip_address': ip_address,
  122                    'network_id': network_id,
  123                    'subnet_id': subnet_id,
  124                    'port_id': port_id})
  125         allocated = port_obj.IPAllocation(
  126             context, network_id=network_id, port_id=port_id,
  127             ip_address=ip_address, subnet_id=subnet_id)
  128         # NOTE(lujinluo): Add IPAllocations obj to the port fixed_ips
  129         # in Port OVO integration, i.e. the same way we did in
  130         # Ib32509d974c8654131112234bcf19d6eae8f7cca
  131         allocated.create()
  132 
  133     def _make_subnet_dict(self, subnet, fields=None, context=None):
  134         res = {'id': subnet['id'],
  135                'name': subnet['name'],
  136                'tenant_id': subnet['tenant_id'],
  137                'network_id': subnet['network_id'],
  138                'ip_version': subnet['ip_version'],
  139                'subnetpool_id': subnet['subnetpool_id'],
  140                'enable_dhcp': subnet['enable_dhcp'],
  141                'ipv6_ra_mode': subnet['ipv6_ra_mode'],
  142                'ipv6_address_mode': subnet['ipv6_address_mode'],
  143                }
  144         res['gateway_ip'] = str(
  145                 subnet['gateway_ip']) if subnet['gateway_ip'] else None
  146         # TODO(korzen) this method can get subnet as DB object or Subnet OVO,
  147         # so temporary workaround will be to fill in the fields in separate
  148         # ways. After converting all code pieces to use Subnet OVO, the latter
  149         # 'else' can be deleted
  150         if isinstance(subnet, subnet_obj.Subnet):
  151             res['cidr'] = str(subnet.cidr)
  152             res['allocation_pools'] = [{'start': str(pool.start),
  153                                        'end': str(pool.end)}
  154                                        for pool in subnet.allocation_pools]
  155             res['host_routes'] = [{'destination': str(route.destination),
  156                                    'nexthop': str(route.nexthop)}
  157                                   for route in subnet.host_routes]
  158             res['dns_nameservers'] = [str(dns.address)
  159                                       for dns in subnet.dns_nameservers]
  160             res['shared'] = subnet.shared
  161             # Call auxiliary extend functions, if any
  162             resource_extend.apply_funcs(subnet_def.COLLECTION_NAME,
  163                                         res, subnet.db_obj)
  164         else:
  165             res['cidr'] = subnet['cidr']
  166             res['allocation_pools'] = [{'start': pool['first_ip'],
  167                                        'end': pool['last_ip']}
  168                                        for pool in subnet['allocation_pools']]
  169             res['host_routes'] = [{'destination': route['destination'],
  170                                    'nexthop': route['nexthop']}
  171                                   for route in subnet['routes']]
  172             res['dns_nameservers'] = [dns['address']
  173                                       for dns in subnet['dns_nameservers']]
  174 
  175             # The shared attribute for a subnet is the same
  176             # as its parent network
  177             res['shared'] = self._is_network_shared(context,
  178                                                     subnet.rbac_entries)
  179             # Call auxiliary extend functions, if any
  180             resource_extend.apply_funcs(subnet_def.COLLECTION_NAME,
  181                                         res, subnet)
  182 
  183         return db_utils.resource_fields(res, fields)
  184 
  185     def _make_subnetpool_dict(self, subnetpool, fields=None):
  186         default_prefixlen = str(subnetpool['default_prefixlen'])
  187         min_prefixlen = str(subnetpool['min_prefixlen'])
  188         max_prefixlen = str(subnetpool['max_prefixlen'])
  189         res = {'id': subnetpool['id'],
  190                'name': subnetpool['name'],
  191                'tenant_id': subnetpool['tenant_id'],
  192                'default_prefixlen': default_prefixlen,
  193                'min_prefixlen': min_prefixlen,
  194                'max_prefixlen': max_prefixlen,
  195                'is_default': subnetpool['is_default'],
  196                'shared': subnetpool['shared'],
  197                'prefixes': [prefix.cidr for prefix in subnetpool['prefixes']],
  198                'ip_version': subnetpool['ip_version'],
  199                'default_quota': subnetpool['default_quota'],
  200                'address_scope_id': subnetpool['address_scope_id']}
  201         resource_extend.apply_funcs(
  202             subnetpool_def.COLLECTION_NAME, res, subnetpool)
  203         return db_utils.resource_fields(res, fields)
  204 
  205     def _make_port_dict(self, port, fields=None,
  206                         process_extensions=True):
  207         mac = port["mac_address"]
  208         if isinstance(mac, netaddr.EUI):
  209             mac.dialect = netaddr.mac_unix_expanded
  210         res = {"id": port["id"],
  211                'name': port['name'],
  212                "network_id": port["network_id"],
  213                'tenant_id': port['tenant_id'],
  214                "mac_address": str(mac),
  215                "admin_state_up": port["admin_state_up"],
  216                "status": port["status"],
  217                "fixed_ips": [{'subnet_id': ip["subnet_id"],
  218                               'ip_address': str(ip["ip_address"])}
  219                              for ip in port["fixed_ips"]],
  220                "device_id": port["device_id"],
  221                "device_owner": port["device_owner"]}
  222         # Call auxiliary extend functions, if any
  223         if process_extensions:
  224             port_data = port
  225             if isinstance(port, port_obj.Port):
  226                 port_data = port.db_obj
  227             resource_extend.apply_funcs(
  228                 port_def.COLLECTION_NAME, res, port_data)
  229         return db_utils.resource_fields(res, fields)
  230 
  231     def _get_network(self, context, id):
  232         try:
  233             network = model_query.get_by_id(context, models_v2.Network, id)
  234         except exc.NoResultFound:
  235             raise exceptions.NetworkNotFound(net_id=id)
  236         return network
  237 
  238     def _get_subnet(self, context, id):
  239         # TODO(slaweq): remove this method when all will be switched to use OVO
  240         # objects only
  241         try:
  242             subnet = model_query.get_by_id(context, models_v2.Subnet, id)
  243         except exc.NoResultFound:
  244             raise exceptions.SubnetNotFound(subnet_id=id)
  245         return subnet
  246 
  247     def _get_subnet_object(self, context, id):
  248         subnet = subnet_obj.Subnet.get_object(context, id=id)
  249         if not subnet:
  250             raise exceptions.SubnetNotFound(subnet_id=id)
  251         return subnet
  252 
  253     def _get_subnetpool(self, context, id):
  254         subnetpool = subnetpool_obj.SubnetPool.get_object(
  255             context, id=id)
  256         if not subnetpool:
  257             raise exceptions.SubnetPoolNotFound(subnetpool_id=id)
  258         return subnetpool
  259 
  260     def _get_port(self, context, id):
  261         try:
  262             port = model_query.get_by_id(context, models_v2.Port, id)
  263         except exc.NoResultFound:
  264             raise exceptions.PortNotFound(port_id=id)
  265         return port
  266 
  267     def _get_route_by_subnet(self, context, subnet_id):
  268         return subnet_obj.Route.get_objects(context,
  269                                             subnet_id=subnet_id)
  270 
  271     def _get_router_gw_ports_by_network(self, context, network_id):
  272         return port_obj.Port.get_objects(
  273             context, network_id=network_id,
  274             device_owner=constants.DEVICE_OWNER_ROUTER_GW)
  275 
  276     @db_api.CONTEXT_READER
  277     def _get_subnets_by_network(self, context, network_id):
  278         return subnet_obj.Subnet.get_objects(context, network_id=network_id)
  279 
  280     @db_api.CONTEXT_READER
  281     def _get_subnets_by_subnetpool(self, context, subnetpool_id):
  282         return subnet_obj.Subnet.get_objects(context,
  283                                              subnetpool_id=subnetpool_id)
  284 
  285     def _get_subnets(self, context, filters=None, fields=None,
  286                      sorts=None, limit=None, marker=None,
  287                      page_reverse=False):
  288         pager = base_obj.Pager(sorts, limit, page_reverse, marker)
  289         filters = filters or {}
  290         # turn the CIDRs into a proper subnets
  291         if filters.get('cidr'):
  292             filters.update(
  293                 {'cidr': [netaddr.IPNetwork(x).cidr for x in filters['cidr']]})
  294         # TODO(ihrachys) remove explicit reader usage when subnet OVO switches
  295         # to engine facade by default
  296         with db_api.CONTEXT_READER.using(context):
  297             return subnet_obj.Subnet.get_objects(context, _pager=pager,
  298                                                  validate_filters=False,
  299                                                  **filters)
  300 
  301     def _make_network_dict(self, network, fields=None,
  302                            process_extensions=True, context=None):
  303         res = {'id': network['id'],
  304                'name': network['name'],
  305                'tenant_id': network['tenant_id'],
  306                'admin_state_up': network['admin_state_up'],
  307                'mtu': network.get('mtu', n_const.DEFAULT_NETWORK_MTU),
  308                'status': network['status'],
  309                'subnets': [subnet['id']
  310                            for subnet in network['subnets']]}
  311         res['shared'] = self._is_network_shared(context, network.rbac_entries)
  312         # Call auxiliary extend functions, if any
  313         if process_extensions:
  314             resource_extend.apply_funcs(net_def.COLLECTION_NAME, res, network)
  315         return db_utils.resource_fields(res, fields)
  316 
  317     def _is_network_shared(self, context, rbac_entries):
  318         # The shared attribute for a network now reflects if the network
  319         # is shared to the calling tenant via an RBAC entry.
  320         matches = ('*',) + ((context.tenant_id,) if context else ())
  321         for entry in rbac_entries:
  322             if (entry.action == 'access_as_shared' and
  323                     entry.target_tenant in matches):
  324                 return True
  325         return False
  326 
  327     def _make_subnet_args(self, detail, subnet, subnetpool_id):
  328         args = {'project_id': detail.tenant_id,
  329                 'id': detail.subnet_id,
  330                 'name': subnet['name'],
  331                 'network_id': subnet['network_id'],
  332                 'ip_version': subnet['ip_version'],
  333                 'cidr': detail.subnet_cidr,
  334                 'subnetpool_id': subnetpool_id,
  335                 'enable_dhcp': subnet['enable_dhcp'],
  336                 'gateway_ip': detail.gateway_ip,
  337                 'description': subnet.get('description')}
  338         if subnet['ip_version'] == 6 and subnet['enable_dhcp']:
  339             if validators.is_attr_set(subnet['ipv6_ra_mode']):
  340                 args['ipv6_ra_mode'] = subnet['ipv6_ra_mode']
  341             if validators.is_attr_set(subnet['ipv6_address_mode']):
  342                 args['ipv6_address_mode'] = subnet['ipv6_address_mode']
  343         return args
  344 
  345     def _make_fixed_ip_dict(self, ips):
  346         # Excludes from dict all keys except subnet_id and ip_address
  347         return [{'subnet_id': ip["subnet_id"],
  348                  'ip_address': ip["ip_address"]}
  349                 for ip in ips]