components/openstack/neutron/files/evs/db/l3nat.py
branchs11-update
changeset 3028 5e73a3a3f66a
child 1944 56ac2df1785b
equal deleted inserted replaced
3027:3bcf7d43558b 3028:5e73a3a3f66a
       
     1 # vim: tabstop=4 shiftwidth=4 softtabstop=4
       
     2 
       
     3 # Copyright 2012 Nicira Networks, Inc.  All rights reserved.
       
     4 #
       
     5 # Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
       
     6 #
       
     7 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
       
     8 #    not use this file except in compliance with the License. You may obtain
       
     9 #    a copy of the License at
       
    10 #
       
    11 #         http://www.apache.org/licenses/LICENSE-2.0
       
    12 #
       
    13 #    Unless required by applicable law or agreed to in writing, software
       
    14 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
       
    15 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
       
    16 #    License for the specific language governing permissions and limitations
       
    17 #    under the License.
       
    18 #
       
    19 # @author: Dan Wendlandt, Nicira, Inc
       
    20 # @author: Girish Moodalbail, Oracle, Inc.
       
    21 #
       
    22 
       
    23 """
       
    24 Based off generic l3_agent (quantum/agent/l3_agent) code
       
    25 """
       
    26 
       
    27 import sqlalchemy as sa
       
    28 
       
    29 from quantum.api.v2 import attributes
       
    30 from quantum.common import constants as l3_constants
       
    31 from quantum.common import exceptions as q_exc
       
    32 from quantum.db import l3_db
       
    33 from quantum.extensions import l3
       
    34 from quantum.openstack.common import log as logging
       
    35 from quantum.openstack.common import uuidutils
       
    36 from quantum.plugins.evs.db import api as evs_db
       
    37 
       
    38 
       
    39 LOG = logging.getLogger(__name__)
       
    40 
       
    41 DEVICE_OWNER_ROUTER_INTF = l3_constants.DEVICE_OWNER_ROUTER_INTF
       
    42 DEVICE_OWNER_ROUTER_GW = l3_constants.DEVICE_OWNER_ROUTER_GW
       
    43 DEVICE_OWNER_FLOATINGIP = l3_constants.DEVICE_OWNER_FLOATINGIP
       
    44 
       
    45 
       
    46 class Router(evs_db.EVS_DB_BASE):
       
    47     """Represents a v2 quantum router."""
       
    48 
       
    49     id = sa.Column(sa.String(36), primary_key=True,
       
    50                    default=uuidutils.generate_uuid)
       
    51     name = sa.Column(sa.String(255))
       
    52     status = sa.Column(sa.String(16))
       
    53     admin_state_up = sa.Column(sa.Boolean)
       
    54     tenant_id = sa.Column(sa.String(255))
       
    55     gw_port_id = sa.Column(sa.String(36))
       
    56     gw_port_network_id = sa.Column(sa.String(36))
       
    57 
       
    58 
       
    59 class FloatingIP(evs_db.EVS_DB_BASE):
       
    60     """Represents a floating IP, which may or many not be
       
    61        allocated to a tenant, and may or may not be associated with
       
    62        an internal port/ip address/router."""
       
    63 
       
    64     id = sa.Column(sa.String(36), primary_key=True,
       
    65                    default=uuidutils.generate_uuid)
       
    66     floating_ip_address = sa.Column(sa.String(64), nullable=False)
       
    67     floating_network_id = sa.Column(sa.String(36), nullable=False)
       
    68     floating_port_id = sa.Column(sa.String(36), nullable=False)
       
    69     fixed_port_id = sa.Column(sa.String(36))
       
    70     fixed_ip_address = sa.Column(sa.String(64))
       
    71     router_id = sa.Column(sa.String(36), sa.ForeignKey('routers.id'))
       
    72     tenant_id = sa.Column(sa.String(255))
       
    73 
       
    74 
       
    75 class EVS_L3_NAT_db_mixin(l3_db.L3_NAT_db_mixin):
       
    76     """Mixin class to add L3/NAT router methods"""
       
    77 
       
    78     Router = Router
       
    79     FloatingIP = FloatingIP
       
    80 
       
    81     def _make_router_dict(self, router, fields=None):
       
    82         res = {'id': router['id'],
       
    83                'name': router['name'],
       
    84                'tenant_id': router['tenant_id'],
       
    85                'admin_state_up': router['admin_state_up'],
       
    86                'status': router['status'],
       
    87                'external_gateway_info': None}
       
    88         if router['gw_port_id']:
       
    89             nw_id = router['gw_port_network_id']
       
    90             res['external_gateway_info'] = {'network_id': nw_id}
       
    91         return self._fields(res, fields)
       
    92 
       
    93     def create_router(self, context, router):
       
    94         return super(EVS_L3_NAT_db_mixin, self).\
       
    95             create_router(evs_db.get_evs_context(context), router)
       
    96 
       
    97     def update_router(self, context, id, router):
       
    98         return super(EVS_L3_NAT_db_mixin, self).\
       
    99             update_router(evs_db.get_evs_context(context), id, router)
       
   100 
       
   101     def _update_router_gw_info(self, context, router_id, info):
       
   102         """This method overrides the base class method and it's contents
       
   103         are exactly same as the base class method except that the Router
       
   104         DB columns are different for EVS and OVS"""
       
   105 
       
   106         router = self._get_router(context, router_id)
       
   107         gw_port_id = router['gw_port_id']
       
   108         gw_port_network_id = router['gw_port_network_id']
       
   109 
       
   110         network_id = info.get('network_id', None) if info else None
       
   111         if network_id:
       
   112             self._get_network(context, network_id)
       
   113             if not self._network_is_external(context, network_id):
       
   114                 msg = _("Network %s is not a valid external "
       
   115                         "network") % network_id
       
   116                 raise q_exc.BadRequest(resource='router', msg=msg)
       
   117 
       
   118         # figure out if we need to delete existing port
       
   119         if gw_port_id and gw_port_network_id != network_id:
       
   120             fip_count = self.get_floatingips_count(context.elevated(),
       
   121                                                    {'router_id': [router_id]})
       
   122             if fip_count:
       
   123                 raise l3.RouterExternalGatewayInUseByFloatingIp(
       
   124                     router_id=router_id, net_id=gw_port_network_id)
       
   125             with context.session.begin(subtransactions=True):
       
   126                 router['gw_port_id'] = None
       
   127                 router['gw_port_network_id'] = None
       
   128                 context.session.add(router)
       
   129             self.delete_port(context.elevated(), gw_port_id,
       
   130                              l3_port_check=False)
       
   131 
       
   132         if network_id is not None and (gw_port_id is None or
       
   133                                        gw_port_network_id != network_id):
       
   134             subnets = self._get_subnets_by_network(context,
       
   135                                                    network_id)
       
   136             for subnet in subnets:
       
   137                 self._check_for_dup_router_subnet(context, router_id,
       
   138                                                   network_id, subnet['id'],
       
   139                                                   subnet['cidr'])
       
   140 
       
   141             # Port has no 'tenant-id', as it is hidden from user
       
   142             gw_port = self.create_port(context.elevated(), {
       
   143                 'port':
       
   144                 {'tenant_id': '',  # intentionally not set
       
   145                  'network_id': network_id,
       
   146                  'mac_address': attributes.ATTR_NOT_SPECIFIED,
       
   147                  'fixed_ips': attributes.ATTR_NOT_SPECIFIED,
       
   148                  'device_id': router_id,
       
   149                  'device_owner': DEVICE_OWNER_ROUTER_GW,
       
   150                  'admin_state_up': True,
       
   151                  'name': ''}})
       
   152 
       
   153             if not len(gw_port['fixed_ips']):
       
   154                 self.delete_port(context.elevated(), gw_port['id'],
       
   155                                  l3_port_check=False)
       
   156                 msg = (_('No IPs available for external network %s') %
       
   157                        network_id)
       
   158                 raise q_exc.BadRequest(resource='router', msg=msg)
       
   159 
       
   160             with context.session.begin(subtransactions=True):
       
   161                 router['gw_port_id'] = gw_port['id']
       
   162                 router['gw_port_network_id'] = gw_port['network_id']
       
   163                 context.session.add(router)
       
   164 
       
   165     def delete_router(self, context, id):
       
   166         super(EVS_L3_NAT_db_mixin, self).\
       
   167             delete_router(evs_db.get_evs_context(context), id)
       
   168 
       
   169     def get_router(self, context, id, fields=None):
       
   170         return super(EVS_L3_NAT_db_mixin, self).\
       
   171             get_router(evs_db.get_evs_context(context), id, fields)
       
   172 
       
   173     def get_routers(self, context, filters=None, fields=None,
       
   174                     sorts=None, limit=None, marker=None, page_reverse=False):
       
   175 
       
   176         query = evs_db.get_session().query(self.Router)
       
   177         if filters is not None:
       
   178             for key, value in filters.iteritems():
       
   179                 column = getattr(self.Router, key, None)
       
   180                 if column:
       
   181                     query = query.filter(column.in_(value))
       
   182 
       
   183         routers = query.all()
       
   184         retlist = []
       
   185         for router in routers:
       
   186             retlist.append(self._make_router_dict(router, fields))
       
   187         return retlist
       
   188 
       
   189     def get_routers_count(self, context, filters=None):
       
   190         return len(self.get_routers(context, filters))
       
   191 
       
   192     def add_router_interface(self, context, router_id, interface_info):
       
   193         return super(EVS_L3_NAT_db_mixin, self).\
       
   194             add_router_interface(evs_db.get_evs_context(context),
       
   195                                  router_id, interface_info)
       
   196 
       
   197     def remove_router_interface(self, context, router_id, interface_info):
       
   198         super(EVS_L3_NAT_db_mixin, self).\
       
   199             remove_router_interface(evs_db.get_evs_context(context),
       
   200                                     router_id, interface_info)
       
   201 
       
   202     def create_floatingip(self, context, floatingip):
       
   203         return super(EVS_L3_NAT_db_mixin, self).\
       
   204             create_floatingip(evs_db.get_evs_context(context), floatingip)
       
   205 
       
   206     def update_floatingip(self, context, id, floatingip):
       
   207         return super(EVS_L3_NAT_db_mixin, self).\
       
   208             update_floatingip(evs_db.get_evs_context(context), id, floatingip)
       
   209 
       
   210     def delete_floatingip(self, context, id):
       
   211         super(EVS_L3_NAT_db_mixin, self).\
       
   212             delete_floatingip(evs_db.get_evs_context(context), id)
       
   213 
       
   214     def get_floatingip(self, context, id, fields=None):
       
   215         return super(EVS_L3_NAT_db_mixin, self).\
       
   216             get_floatingip(evs_db.get_evs_context(context), id, fields)
       
   217 
       
   218     def get_floatingips(self, context, filters=None, fields=None,
       
   219                         sorts=None, limit=None, marker=None,
       
   220                         page_reverse=False):
       
   221 
       
   222         query = evs_db.get_session().query(self.FloatingIP)
       
   223         if filters:
       
   224             for key, value in filters.iteritems():
       
   225                 column = getattr(self.FloatingIP, key, None)
       
   226                 if column:
       
   227                     query = query.filter(column.in_(value))
       
   228 
       
   229         floatingips = query.all()
       
   230         retlist = []
       
   231         for floatingip in floatingips:
       
   232             retlist.append(self._make_floatingip_dict(floatingip, fields))
       
   233         return retlist
       
   234 
       
   235     def get_floatingips_count(self, context, filters=None):
       
   236         return len(self.get_floatingips(context, filters))
       
   237 
       
   238     def prevent_l3_port_deletion(self, context, port_id):
       
   239         """ Checks to make sure a port is allowed to be deleted, raising
       
   240         an exception if this is not the case.  This should be called by
       
   241         any plugin when the API requests the deletion of a port, since
       
   242         some ports for L3 are not intended to be deleted directly via a
       
   243         DELETE to /ports, but rather via other API calls that perform the
       
   244         proper deletion checks.
       
   245         """
       
   246         port = self.get_port(context, port_id)
       
   247         if port['device_owner'] in [DEVICE_OWNER_ROUTER_INTF,
       
   248                                     DEVICE_OWNER_ROUTER_GW,
       
   249                                     DEVICE_OWNER_FLOATINGIP]:
       
   250             raise l3.L3PortInUse(port_id=port_id,
       
   251                                  device_owner=port['device_owner'])
       
   252 
       
   253     def disassociate_floatingips(self, context, port_id):
       
   254         super(EVS_L3_NAT_db_mixin, self).\
       
   255             disassociate_floatingips(evs_db.get_evs_context(context), port_id)
       
   256 
       
   257     def _network_is_external(self, context, net_id):
       
   258         try:
       
   259             evs = self.get_network(context, net_id)
       
   260             return evs[l3.EXTERNAL]
       
   261         except:
       
   262             return False
       
   263 
       
   264     def get_sync_data(self, context, router_ids=None, active=None):
       
   265         return super(EVS_L3_NAT_db_mixin, self).\
       
   266             get_sync_data(evs_db.get_evs_context(context), router_ids, active)
       
   267 
       
   268     def get_external_network_id(self, context):
       
   269         return super(EVS_L3_NAT_db_mixin, self).\
       
   270             get_external_network_id(evs_db.get_evs_context(context))
       
   271 
       
   272     def _get_tenant_id_for_create(self, context, resource):
       
   273         if context.is_admin and 'tenant_id' in resource:
       
   274             tenant_id = resource['tenant_id']
       
   275         elif ('tenant_id' in resource and
       
   276               resource['tenant_id'] != context.tenant_id):
       
   277             reason = _('Cannot create resource for another tenant')
       
   278             raise q_exc.AdminRequired(reason=reason)
       
   279         else:
       
   280             tenant_id = context.tenant_id
       
   281         return tenant_id
       
   282 
       
   283     def _get_by_id(self, context, model, id):
       
   284         return context.session.query(model).\
       
   285             filter(model.id == id).one()
       
   286 
       
   287     def _get_network(self, context, network_id):
       
   288         return self.get_network(context, network_id)
       
   289 
       
   290     def _get_subnet(self, context, subnet_id):
       
   291         return self.get_subnet(context, subnet_id)
       
   292 
       
   293     def _get_port(self, context, port_id):
       
   294         return self.get_port(context, port_id)
       
   295 
       
   296     def _delete_port(self, context, port_id):
       
   297         return self.delete_port(context, port_id)
       
   298 
       
   299     def _get_subnets_by_network(self, context, network_id):
       
   300         return self.get_subnets(context, filters={'network_id': network_id})
       
   301 
       
   302     def allow_l3_port_deletion(self, context, port_id):
       
   303         """ If an L3 agent is using this port, then we need to send
       
   304         a notification to L3 agent to release the port before we can
       
   305         delete the port"""
       
   306         pass