483cc047fa
Some time ago in patch [1] deprecated option 'gateway_external_network_id' was removed. After that commit [2] removed rpc function "get_external_network_id" but there still left some leftovers from this. This patch removes them completly. [1] https://review.opendev.org/#/c/666409/ [2] https://review.opendev.org/#/c/682418/ Depends-On: https://review.opendev.org/710429 Change-Id: Ie58ea7f021db051b68be80a1d98f5985ff19fe23
248 lines
11 KiB
Python
248 lines
11 KiB
Python
# Copyright (c) 2013 OpenStack Foundation.
|
|
# All Rights Reserved.
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
# not use this file except in compliance with the License. You may obtain
|
|
# a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
# License for the specific language governing permissions and limitations
|
|
# under the License.
|
|
|
|
from neutron_lib.api.definitions import external_net as extnet_apidef
|
|
from neutron_lib.api.definitions import network as net_def
|
|
from neutron_lib.api import validators
|
|
from neutron_lib.callbacks import events
|
|
from neutron_lib.callbacks import registry
|
|
from neutron_lib.callbacks import resources
|
|
from neutron_lib import constants
|
|
from neutron_lib.db import model_query
|
|
from neutron_lib.db import resource_extend
|
|
from neutron_lib.db import utils as db_utils
|
|
from neutron_lib import exceptions as n_exc
|
|
from neutron_lib.exceptions import external_net as extnet_exc
|
|
from neutron_lib.plugins import constants as plugin_constants
|
|
from neutron_lib.plugins import directory
|
|
from sqlalchemy.sql import expression as expr
|
|
|
|
from neutron._i18n import _
|
|
from neutron.db import models_v2
|
|
from neutron.extensions import rbac as rbac_ext
|
|
from neutron.objects import network as net_obj
|
|
from neutron.objects import router as l3_obj
|
|
|
|
|
|
def _network_filter_hook(context, original_model, conditions):
|
|
if conditions is not None and not hasattr(conditions, '__iter__'):
|
|
conditions = (conditions, )
|
|
# Apply the external network filter only in non-admin and non-advsvc
|
|
# context
|
|
if db_utils.model_query_scope_is_project(context, original_model):
|
|
# the table will already be joined to the rbac entries for the
|
|
# shared check so we don't need to worry about ensuring that
|
|
rbac_model = original_model.rbac_entries.property.mapper.class_
|
|
tenant_allowed = (
|
|
(rbac_model.action == 'access_as_external') &
|
|
(rbac_model.target_tenant == context.tenant_id) |
|
|
(rbac_model.target_tenant == '*'))
|
|
conditions = expr.or_(tenant_allowed, *conditions)
|
|
return conditions
|
|
|
|
|
|
def _network_result_filter_hook(query, filters):
|
|
vals = filters and filters.get(extnet_apidef.EXTERNAL, [])
|
|
if not vals:
|
|
return query
|
|
if vals[0]:
|
|
return query.filter(models_v2.Network.external.has())
|
|
return query.filter(~models_v2.Network.external.has())
|
|
|
|
|
|
@resource_extend.has_resource_extenders
|
|
@registry.has_registry_receivers
|
|
class External_net_db_mixin(object):
|
|
"""Mixin class to add external network methods to db_base_plugin_v2."""
|
|
|
|
def __new__(cls, *args, **kwargs):
|
|
model_query.register_hook(
|
|
models_v2.Network,
|
|
"external_net",
|
|
query_hook=None,
|
|
filter_hook=_network_filter_hook,
|
|
result_filters=_network_result_filter_hook)
|
|
return super(External_net_db_mixin, cls).__new__(cls, *args, **kwargs)
|
|
|
|
def _network_is_external(self, context, net_id):
|
|
return net_obj.ExternalNetwork.objects_exist(
|
|
context, network_id=net_id)
|
|
|
|
@staticmethod
|
|
@resource_extend.extends([net_def.COLLECTION_NAME])
|
|
def _extend_network_dict_l3(network_res, network_db):
|
|
# Comparing with None for converting uuid into bool
|
|
network_res[extnet_apidef.EXTERNAL] = network_db.external is not None
|
|
return network_res
|
|
|
|
def _process_l3_create(self, context, net_data, req_data):
|
|
external = req_data.get(extnet_apidef.EXTERNAL)
|
|
external_set = validators.is_attr_set(external)
|
|
|
|
if not external_set:
|
|
return
|
|
|
|
if external:
|
|
net_obj.ExternalNetwork(
|
|
context, network_id=net_data['id']).create()
|
|
net_rbac_args = {'project_id': net_data['tenant_id'],
|
|
'object_id': net_data['id'],
|
|
'action': 'access_as_external',
|
|
'target_tenant': '*'}
|
|
net_obj.NetworkRBAC(context, **net_rbac_args).create()
|
|
net_data[extnet_apidef.EXTERNAL] = external
|
|
|
|
def _process_l3_update(self, context, net_data, req_data, allow_all=True):
|
|
new_value = req_data.get(extnet_apidef.EXTERNAL)
|
|
net_id = net_data['id']
|
|
if not validators.is_attr_set(new_value):
|
|
return
|
|
|
|
if net_data.get(extnet_apidef.EXTERNAL) == new_value:
|
|
return
|
|
|
|
if new_value:
|
|
net_obj.ExternalNetwork(
|
|
context, network_id=net_id).create()
|
|
net_data[extnet_apidef.EXTERNAL] = True
|
|
if allow_all:
|
|
net_rbac_args = {'project_id': net_data['tenant_id'],
|
|
'object_id': net_id,
|
|
'action': 'access_as_external',
|
|
'target_tenant': '*'}
|
|
net_obj.NetworkRBAC(context, **net_rbac_args).create()
|
|
else:
|
|
# must make sure we do not have any external gateway ports
|
|
# (and thus, possible floating IPs) on this network before
|
|
# allow it to be update to external=False
|
|
if context.session.query(models_v2.Port.id).filter_by(
|
|
device_owner=constants.DEVICE_OWNER_ROUTER_GW,
|
|
network_id=net_data['id']).first():
|
|
raise extnet_exc.ExternalNetworkInUse(net_id=net_id)
|
|
|
|
net_obj.ExternalNetwork.delete_objects(
|
|
context, network_id=net_id)
|
|
net_obj.NetworkRBAC.delete_objects(
|
|
context, object_id=net_id, action='access_as_external')
|
|
net_data[extnet_apidef.EXTERNAL] = False
|
|
|
|
def _process_l3_delete(self, context, network_id):
|
|
l3plugin = directory.get_plugin(plugin_constants.L3)
|
|
if l3plugin:
|
|
l3plugin.delete_disassociated_floatingips(context, network_id)
|
|
|
|
@registry.receives(resources.RBAC_POLICY, [events.BEFORE_CREATE])
|
|
def _process_ext_policy_create(self, resource, event, trigger,
|
|
payload=None):
|
|
object_type = payload.metadata.get('object_type')
|
|
policy = payload.request_body
|
|
context = payload.context
|
|
|
|
if (object_type != 'network' or
|
|
policy['action'] != 'access_as_external'):
|
|
return
|
|
net = self.get_network(context, policy['object_id'])
|
|
if not context.is_admin and net['tenant_id'] != context.tenant_id:
|
|
msg = _("Only admins can manipulate policies on networks they "
|
|
"do not own")
|
|
raise n_exc.InvalidInput(error_message=msg)
|
|
if not self._network_is_external(context, policy['object_id']):
|
|
# we automatically convert the network into an external network
|
|
self._process_l3_update(context, net,
|
|
{extnet_apidef.EXTERNAL: True},
|
|
allow_all=False)
|
|
|
|
@registry.receives(resources.RBAC_POLICY, [events.AFTER_DELETE])
|
|
def _process_ext_policy_delete(self, resource, event, trigger,
|
|
payload=None):
|
|
object_type = payload.metadata.get('object_type')
|
|
policy = payload.latest_state
|
|
context = payload.context
|
|
|
|
if (object_type != 'network' or
|
|
policy['action'] != 'access_as_external'):
|
|
return
|
|
# If the network still have rbac policies, we should not
|
|
# update external attribute.
|
|
if net_obj.NetworkRBAC.count(context, object_id=policy['object_id'],
|
|
action='access_as_external'):
|
|
return
|
|
net = self.get_network(context, policy['object_id'])
|
|
self._process_l3_update(context, net,
|
|
{extnet_apidef.EXTERNAL: False})
|
|
|
|
@registry.receives(resources.RBAC_POLICY, (events.BEFORE_UPDATE,
|
|
events.BEFORE_DELETE))
|
|
def _validate_ext_not_in_use_by_tenant(self, resource, event, trigger,
|
|
payload=None):
|
|
object_type = payload.metadata.get('object_type')
|
|
policy = payload.latest_state
|
|
context = payload.context
|
|
|
|
if (object_type != 'network' or
|
|
policy['action'] != 'access_as_external'):
|
|
return
|
|
new_project = None
|
|
if event == events.BEFORE_UPDATE:
|
|
new_project = payload.request_body['target_tenant']
|
|
if new_project == policy['target_tenant']:
|
|
# nothing to validate if the tenant didn't change
|
|
return
|
|
gw_ports = context.session.query(models_v2.Port.id).filter_by(
|
|
device_owner=constants.DEVICE_OWNER_ROUTER_GW,
|
|
network_id=policy['object_id'])
|
|
gw_ports = [gw_port[0] for gw_port in gw_ports]
|
|
if policy['target_tenant'] != '*':
|
|
filters = {
|
|
'gw_port_id': gw_ports,
|
|
'project_id': policy['target_tenant']
|
|
}
|
|
# if there is a wildcard entry we can safely proceed without the
|
|
# router lookup because they will have access either way
|
|
if net_obj.NetworkRBAC.count(
|
|
context, object_id=policy['object_id'],
|
|
action='access_as_external', target_tenant='*'):
|
|
return
|
|
router_exist = l3_obj.Router.objects_exist(context, **filters)
|
|
else:
|
|
# deleting the wildcard is okay as long as the tenants with
|
|
# attached routers have their own entries and the network is
|
|
# not the default external network.
|
|
if net_obj.ExternalNetwork.objects_exist(
|
|
context, network_id=policy['object_id'], is_default=True):
|
|
msg = _("Default external networks must be shared to "
|
|
"everyone.")
|
|
raise rbac_ext.RbacPolicyInUse(object_id=policy['object_id'],
|
|
details=msg)
|
|
projects = net_obj.NetworkRBAC.get_projects(
|
|
context, object_id=policy['object_id'],
|
|
action='access_as_external')
|
|
projects_with_entries = [project for project in projects
|
|
if project != '*']
|
|
if new_project:
|
|
projects_with_entries.append(new_project)
|
|
router_exist = l3_obj.Router.check_routers_not_owned_by_projects(
|
|
context, gw_ports, projects_with_entries)
|
|
if router_exist:
|
|
msg = _("There are routers attached to this network that "
|
|
"depend on this policy for access.")
|
|
raise rbac_ext.RbacPolicyInUse(object_id=policy['object_id'],
|
|
details=msg)
|
|
|
|
@registry.receives(resources.NETWORK, [events.BEFORE_DELETE])
|
|
def _before_network_delete_handler(self, resource, event, trigger,
|
|
payload=None):
|
|
self._process_l3_delete(payload.context, payload.resource_id)
|