1130 lines
50 KiB
Python
1130 lines
50 KiB
Python
#
|
|
# 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.
|
|
|
|
import netaddr
|
|
from neutron.api.rpc.agentnotifiers import dhcp_rpc_agent_api
|
|
from neutron.api.rpc.agentnotifiers import l3_rpc_agent_api
|
|
from neutron.api.rpc.handlers import dhcp_rpc
|
|
from neutron.api.rpc.handlers import l3_rpc
|
|
from neutron.api.rpc.handlers import metadata_rpc
|
|
from neutron.api.v2 import attributes as attr
|
|
from neutron.callbacks import events
|
|
from neutron.callbacks import registry
|
|
from neutron.callbacks import resources
|
|
from neutron.common import exceptions as n_common_exc
|
|
from neutron.common import rpc as n_rpc
|
|
from neutron.common import topics
|
|
from neutron.common import utils
|
|
from neutron.db import agents_db
|
|
from neutron.db import agentschedulers_db
|
|
from neutron.db import allowedaddresspairs_db as addr_pair_db
|
|
from neutron.db import db_base_plugin_v2
|
|
from neutron.db import external_net_db
|
|
from neutron.db import extradhcpopt_db
|
|
from neutron.db import extraroute_db
|
|
from neutron.db import l3_agentschedulers_db
|
|
from neutron.db import l3_attrs_db
|
|
from neutron.db import l3_db
|
|
from neutron.db import l3_gwmode_db
|
|
from neutron.db.models import securitygroup as sg_models
|
|
from neutron.db import models_v2
|
|
from neutron.db import netmtu_db
|
|
from neutron.db import portbindings_db
|
|
from neutron.db import portsecurity_db_common
|
|
from neutron.db import securitygroups_db
|
|
from neutron.extensions import allowedaddresspairs as addr_pair
|
|
from neutron.extensions import extra_dhcp_opt as edo_ext
|
|
from neutron.extensions import portbindings
|
|
from neutron.extensions import portsecurity as psec
|
|
from neutron.extensions import providernet as pnet
|
|
from neutron.quota import resource_registry
|
|
from neutron_lib import constants as const
|
|
from neutron_lib import exceptions as n_exc
|
|
from oslo_config import cfg
|
|
from oslo_log import log
|
|
from oslo_utils import excutils
|
|
from oslo_utils import importutils
|
|
import six
|
|
from sqlalchemy.orm import exc as sa_exc
|
|
|
|
from dragonflow._i18n import _, _LE, _LI
|
|
from dragonflow.common import common_params
|
|
from dragonflow.common import constants as df_common_const
|
|
from dragonflow.common import exceptions as df_exceptions
|
|
from dragonflow.common import extensions
|
|
from dragonflow.common import utils as df_utils
|
|
from dragonflow.db import api_nb
|
|
from dragonflow.db.neutron import lockedobjects_db as lock_db
|
|
from dragonflow.db.neutron import versionobjects_db as version_db
|
|
from dragonflow.neutron.common import constants as df_const
|
|
|
|
LOG = log.getLogger(__name__)
|
|
|
|
cfg.CONF.register_opts(common_params.DF_OPTS, 'df')
|
|
|
|
router_distributed_opts = [
|
|
cfg.BoolOpt('router_distributed',
|
|
default=False,
|
|
help=_("System-wide flag to determine the type of router "
|
|
"that tenants can create. Only admin can override.")),
|
|
]
|
|
cfg.CONF.register_opts(router_distributed_opts)
|
|
|
|
|
|
class DFPlugin(db_base_plugin_v2.NeutronDbPluginV2,
|
|
securitygroups_db.SecurityGroupDbMixin,
|
|
l3_agentschedulers_db.L3AgentSchedulerDbMixin,
|
|
l3_gwmode_db.L3_NAT_db_mixin,
|
|
l3_attrs_db.ExtraAttributesMixin,
|
|
external_net_db.External_net_db_mixin,
|
|
portbindings_db.PortBindingMixin,
|
|
portsecurity_db_common.PortSecurityDbCommon,
|
|
addr_pair_db.AllowedAddressPairsMixin,
|
|
extradhcpopt_db.ExtraDhcpOptMixin,
|
|
extraroute_db.ExtraRoute_db_mixin,
|
|
agentschedulers_db.DhcpAgentSchedulerDbMixin,
|
|
netmtu_db.Netmtu_db_mixin):
|
|
|
|
__native_bulk_support = True
|
|
__native_pagination_support = True
|
|
__native_sorting_support = True
|
|
|
|
supported_extension_aliases = extensions.SUPPORTED_API_EXTENSIONS
|
|
|
|
extra_attributes = (
|
|
l3_attrs_db.ExtraAttributesMixin.extra_attributes + [{
|
|
'name': "distributed",
|
|
'default': cfg.CONF.router_distributed
|
|
}])
|
|
|
|
@resource_registry.tracked_resources(
|
|
network=models_v2.Network,
|
|
port=models_v2.Port,
|
|
subnet=models_v2.Subnet,
|
|
subnetpool=models_v2.SubnetPool,
|
|
security_group=sg_models.SecurityGroup,
|
|
security_group_rule=sg_models.SecurityGroupRule,
|
|
router=l3_db.Router,
|
|
floatingip=l3_db.FloatingIP)
|
|
def __init__(self):
|
|
self.router_scheduler = importutils.import_object(
|
|
cfg.CONF.router_scheduler_driver)
|
|
super(DFPlugin, self).__init__()
|
|
LOG.info(_LI("Starting DFPlugin"))
|
|
self.vif_type = portbindings.VIF_TYPE_OVS
|
|
self._set_base_port_binding()
|
|
# When set to True, Nova plugs the VIF directly into the ovs bridge
|
|
# instead of using the hybrid mode.
|
|
self.vif_details = {portbindings.CAP_PORT_FILTER: True}
|
|
registry.subscribe(self.post_fork_initialize, resources.PROCESS,
|
|
events.AFTER_INIT)
|
|
|
|
self._setup_dhcp()
|
|
self._start_rpc_notifiers()
|
|
|
|
def post_fork_initialize(self, resource, event, trigger, **kwargs):
|
|
nb_driver = df_utils.load_driver(
|
|
cfg.CONF.df.nb_db_class,
|
|
df_utils.DF_NB_DB_DRIVER_NAMESPACE)
|
|
|
|
self.nb_api = api_nb.NbApi(
|
|
nb_driver,
|
|
use_pubsub=cfg.CONF.df.enable_df_pub_sub,
|
|
is_neutron_server=True)
|
|
self.nb_api.initialize(db_ip=cfg.CONF.df.remote_db_ip,
|
|
db_port=cfg.CONF.df.remote_db_port)
|
|
|
|
self._set_base_port_binding()
|
|
|
|
def _set_base_port_binding(self):
|
|
if cfg.CONF.df.vif_type == portbindings.VIF_TYPE_VHOST_USER:
|
|
self.base_binding_dict = {
|
|
portbindings.VIF_TYPE: portbindings.VIF_TYPE_VHOST_USER,
|
|
portbindings.VIF_DETAILS: {
|
|
# TODO(nick-ma-z): VIF security is disabled for vhu port.
|
|
# This will be revisited if the function is supported by
|
|
# OVS upstream.
|
|
portbindings.CAP_PORT_FILTER: False,
|
|
portbindings.VHOST_USER_MODE:
|
|
portbindings.VHOST_USER_MODE_CLIENT,
|
|
portbindings.VHOST_USER_OVS_PLUG: True,
|
|
}
|
|
}
|
|
else:
|
|
self.base_binding_dict = {
|
|
portbindings.VIF_TYPE: portbindings.VIF_TYPE_OVS,
|
|
portbindings.VIF_DETAILS: {
|
|
# TODO(rkukura): Replace with new VIF security details
|
|
portbindings.CAP_PORT_FILTER:
|
|
'security-group' in self.supported_extension_aliases}}
|
|
|
|
def _update_port_binding(self, port_res):
|
|
port_res[portbindings.VNIC_TYPE] = portbindings.VNIC_NORMAL
|
|
if cfg.CONF.df.vif_type == portbindings.VIF_TYPE_VHOST_USER:
|
|
port_res[portbindings.VIF_DETAILS].update({
|
|
portbindings.VHOST_USER_SOCKET: utils.get_vhu_sockpath(
|
|
cfg.CONF.df.vhost_sock_dir, port_res['id']
|
|
)
|
|
})
|
|
|
|
def _setup_dhcp(self):
|
|
"""Initialize components to support DHCP."""
|
|
if cfg.CONF.df.use_centralized_ipv6_DHCP:
|
|
self.network_scheduler = importutils.import_object(
|
|
cfg.CONF.network_scheduler_driver
|
|
)
|
|
self.start_periodic_dhcp_agent_status_check()
|
|
|
|
def _setup_rpc(self):
|
|
|
|
self.endpoints = [l3_rpc.L3RpcCallback(),
|
|
agents_db.AgentExtRpcCallback(),
|
|
metadata_rpc.MetadataRpcCallback()]
|
|
if cfg.CONF.df.use_centralized_ipv6_DHCP:
|
|
self.endpoints.append(dhcp_rpc.DhcpRpcCallback())
|
|
|
|
def _start_rpc_notifiers(self):
|
|
"""Initialize RPC notifiers for agents."""
|
|
|
|
if cfg.CONF.df.use_centralized_ipv6_DHCP:
|
|
self.agent_notifiers[const.AGENT_TYPE_DHCP] = (
|
|
dhcp_rpc_agent_api.DhcpAgentNotifyAPI()
|
|
)
|
|
|
|
self.agent_notifiers[const.AGENT_TYPE_L3] = (
|
|
l3_rpc_agent_api.L3AgentNotifyAPI()
|
|
)
|
|
|
|
def start_rpc_listeners(self):
|
|
self._setup_rpc()
|
|
self.conn = n_rpc.create_connection()
|
|
self.conn.create_consumer(topics.PLUGIN, self.endpoints, fanout=False)
|
|
self.conn.create_consumer(topics.L3PLUGIN, self.endpoints,
|
|
fanout=False)
|
|
# topics.REPORTS was added for the Mitaka release, therefore, to
|
|
# work with stable/liberty, check to see if topics.REPORTS exists
|
|
# if it does, use it.
|
|
if hasattr(topics, 'REPORTS'):
|
|
self.conn.create_consumer(
|
|
topics.REPORTS, [agents_db.AgentExtRpcCallback()],
|
|
fanout=False)
|
|
return self.conn.consume_in_threads()
|
|
|
|
def _delete_ports(self, context, ports):
|
|
for port in ports:
|
|
try:
|
|
self.delete_port(context, port.id)
|
|
except (n_exc.PortNotFound, sa_exc.ObjectDeletedError):
|
|
context.session.expunge(port)
|
|
# concurrent port deletion can be performed by
|
|
# release_dhcp_port caused by concurrent subnet_delete
|
|
LOG.info(_LI("Port %s was deleted concurrently"), port.id)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception():
|
|
LOG.exception(_LE("Exception auto-deleting port %s"),
|
|
port.id)
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def create_security_group(self, context, security_group,
|
|
default_sg=False):
|
|
with context.session.begin(subtransactions=True):
|
|
sg_db = super(DFPlugin,
|
|
self).create_security_group(context, security_group,
|
|
default_sg)
|
|
sg_version = version_db._create_db_version_row(
|
|
context.session, sg_db['id'])
|
|
sg_id = sg_db['id']
|
|
sg_name = sg_db.get('name', df_const.DF_SG_DEFAULT_NAME)
|
|
tenant_id = sg_db['tenant_id']
|
|
rules = sg_db.get('security_group_rules')
|
|
for rule in rules:
|
|
rule['topic'] = rule.get('tenant_id')
|
|
del rule['tenant_id']
|
|
|
|
self.nb_api.create_security_group(id=sg_id, topic=tenant_id,
|
|
name=sg_name, rules=rules,
|
|
version=sg_version)
|
|
return sg_db
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def update_security_group(self, context, sg_id, security_group):
|
|
with context.session.begin(subtransactions=True):
|
|
sg_db = super(DFPlugin,
|
|
self).update_security_group(context, sg_id,
|
|
security_group)
|
|
sg_version = version_db._update_db_version_row(
|
|
context.session, sg_id)
|
|
|
|
sg_name = sg_db.get('name', df_const.DF_SG_DEFAULT_NAME)
|
|
tenant_id = sg_db['tenant_id']
|
|
rules = sg_db.get('security_group_rules')
|
|
|
|
self.nb_api.update_security_group(id=sg_id, topic=tenant_id,
|
|
name=sg_name, rules=rules,
|
|
version=sg_version)
|
|
return sg_db
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def create_security_group_rule(self, context, security_group_rule):
|
|
with context.session.begin(subtransactions=True):
|
|
sg_rule = super(DFPlugin, self).create_security_group_rule(
|
|
context, security_group_rule)
|
|
sg_id = sg_rule['security_group_id']
|
|
sg_version_id = version_db._update_db_version_row(
|
|
context.session, sg_id)
|
|
sg_group = self.get_security_group(context, sg_id)
|
|
sg_rule['topic'] = sg_rule.get('tenant_id')
|
|
del sg_rule['tenant_id']
|
|
self.nb_api.add_security_group_rules(sg_id, sg_group['tenant_id'],
|
|
sg_rules=[sg_rule],
|
|
sg_version=sg_version_id)
|
|
return sg_rule
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def delete_security_group_rule(self, context, id):
|
|
with context.session.begin(subtransactions=True):
|
|
security_group_rule = self.get_security_group_rule(context, id)
|
|
sg_id = security_group_rule['security_group_id']
|
|
sg_group = self.get_security_group(context, sg_id)
|
|
super(DFPlugin, self).delete_security_group_rule(context, id)
|
|
sg_version_id = version_db._update_db_version_row(
|
|
context.session, sg_id)
|
|
self.nb_api.delete_security_group_rule(sg_id, id,
|
|
sg_group['tenant_id'],
|
|
sg_version=sg_version_id)
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def delete_security_group(self, context, sg_id):
|
|
sg = self.get_security_group(context, sg_id)
|
|
tenant_id = sg['tenant_id']
|
|
with context.session.begin(subtransactions=True):
|
|
super(DFPlugin, self).delete_security_group(context, sg_id)
|
|
version_db._delete_db_version_row(
|
|
context.session, sg_id)
|
|
self.nb_api.delete_security_group(sg_id, topic=tenant_id)
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def create_subnet(self, context, subnet):
|
|
net_id = subnet['subnet']['network_id']
|
|
new_subnet = None
|
|
dhcp_ip = None
|
|
dhcp_port = None
|
|
network_version = None
|
|
|
|
try:
|
|
with context.session.begin(subtransactions=True):
|
|
# create subnet in DB
|
|
new_subnet = super(DFPlugin,
|
|
self).create_subnet(context, subnet)
|
|
dhcp_ip, dhcp_port = self._handle_create_subnet_dhcp(
|
|
context, new_subnet)
|
|
network_version = version_db._update_db_version_row(
|
|
context.session, net_id)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception() as ctxt:
|
|
ctxt.reraise = True
|
|
# delete the stale dhcp port
|
|
try:
|
|
if dhcp_port:
|
|
self.nb_api.delete_lport(dhcp_port['id'],
|
|
dhcp_port['tenant_id'])
|
|
except df_exceptions.DBKeyNotFound:
|
|
pass
|
|
|
|
if new_subnet:
|
|
self.nb_api.add_subnet(
|
|
new_subnet['id'],
|
|
net_id,
|
|
new_subnet['tenant_id'],
|
|
name=new_subnet.get('name', df_const.DF_SUBNET_DEFAULT_NAME),
|
|
nw_version=network_version,
|
|
enable_dhcp=new_subnet['enable_dhcp'],
|
|
cidr=new_subnet['cidr'],
|
|
dhcp_ip=dhcp_ip,
|
|
gateway_ip=new_subnet['gateway_ip'],
|
|
dns_nameservers=new_subnet.get('dns_nameservers', []),
|
|
host_routes=new_subnet.get('host_routes', []))
|
|
return new_subnet
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def update_subnet(self, context, id, subnet):
|
|
dhcp_ip = None
|
|
dhcp_port = None
|
|
new_subnet = None
|
|
net_id = None
|
|
network_version = None
|
|
try:
|
|
with context.session.begin(subtransactions=True):
|
|
# update subnet in DB
|
|
original_subnet = super(DFPlugin, self).get_subnet(context, id)
|
|
new_subnet = super(DFPlugin,
|
|
self).update_subnet(context, id, subnet)
|
|
net_id = new_subnet['network_id']
|
|
dhcp_ip, dhcp_port = self._update_subnet_dhcp(
|
|
context, original_subnet, new_subnet)
|
|
network_version = version_db._update_db_version_row(
|
|
context.session, net_id)
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception() as ctxt:
|
|
ctxt.reraise = True
|
|
# delete the stale dhcp port
|
|
try:
|
|
if dhcp_port:
|
|
self.nb_api.delete_lport(dhcp_port['id'],
|
|
dhcp_port['tenant_id'])
|
|
except df_exceptions.DBKeyNotFound:
|
|
pass
|
|
|
|
if new_subnet and net_id:
|
|
# update df controller with subnet
|
|
self.nb_api.update_subnet(
|
|
new_subnet['id'],
|
|
net_id,
|
|
new_subnet['tenant_id'],
|
|
name=new_subnet.get('name', df_const.DF_SUBNET_DEFAULT_NAME),
|
|
nw_version=network_version,
|
|
enable_dhcp=new_subnet['enable_dhcp'],
|
|
cidr=new_subnet['cidr'],
|
|
dhcp_ip=dhcp_ip,
|
|
gateway_ip=new_subnet['gateway_ip'],
|
|
dns_nameservers=new_subnet.get('dns_nameservers', []),
|
|
host_routes=new_subnet.get('host_routes', []))
|
|
return new_subnet
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def delete_subnet(self, context, id):
|
|
orig_subnet = super(DFPlugin, self).get_subnet(context, id)
|
|
net_id = orig_subnet['network_id']
|
|
with context.session.begin(subtransactions=True):
|
|
# delete subnet in DB
|
|
super(DFPlugin, self).delete_subnet(context, id)
|
|
network_version = version_db._update_db_version_row(
|
|
context.session, net_id)
|
|
|
|
# update df controller with subnet delete
|
|
if net_id:
|
|
try:
|
|
self.nb_api.delete_subnet(id, net_id,
|
|
orig_subnet['tenant_id'],
|
|
nw_version=network_version)
|
|
except df_exceptions.DBKeyNotFound:
|
|
LOG.debug("network %s is not found in DB, might have "
|
|
"been deleted concurrently" % net_id)
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def create_network(self, context, network):
|
|
with context.session.begin(subtransactions=True):
|
|
result = super(DFPlugin, self).create_network(context,
|
|
network)
|
|
data = network['network']
|
|
if psec.PORTSECURITY not in data:
|
|
data[psec.PORTSECURITY] = \
|
|
(psec.EXTENDED_ATTRIBUTES_2_0['networks']
|
|
[psec.PORTSECURITY]['default'])
|
|
self._process_network_port_security_create(context, data, result)
|
|
self._process_l3_create(context, result, data)
|
|
nw_version = version_db._create_db_version_row(
|
|
context.session, result['id'])
|
|
self._create_network_nb_api(context, result, nw_version)
|
|
return result
|
|
|
|
def _create_network_nb_api(self, context, network, nw_version):
|
|
nw_name = network.get('name', df_const.DF_NETWORK_DEFAULT_NAME)
|
|
self.nb_api.create_lswitch(id=network['id'],
|
|
topic=network['tenant_id'],
|
|
name=nw_name,
|
|
router_external=network['router:external'],
|
|
mtu=network['mtu'],
|
|
version=nw_version,
|
|
subnets=[])
|
|
return network
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def delete_network(self, context, network_id):
|
|
dhcp_ports = []
|
|
with context.session.begin(subtransactions=True):
|
|
network = self.get_network(context, network_id)
|
|
tenant_id = network['tenant_id']
|
|
try:
|
|
lswitch = self.nb_api.get_lswitch(id=network_id,
|
|
topic=tenant_id)
|
|
except df_exceptions.DBKeyNotFound:
|
|
LOG.debug("lswitch %s is not found in DF DB, might have "
|
|
"been deleted concurrently" % network_id)
|
|
if lswitch is not None:
|
|
subnets = [subnet.get_id() for subnet in lswitch.get_subnets()]
|
|
dhcp_ports = self._get_ports_by_subnets_and_owners(
|
|
context, subnets, [const.DEVICE_OWNER_DHCP])
|
|
super(DFPlugin, self).delete_network(context, network_id)
|
|
version_db._delete_db_version_row(context.session, network_id)
|
|
|
|
for port in dhcp_ports:
|
|
try:
|
|
self.nb_api.delete_lport(id=port['id'],
|
|
topic=port['tenant_id'])
|
|
except df_exceptions.DBKeyNotFound:
|
|
LOG.debug("port %s is not found in DB, might have"
|
|
"been deleted concurrently" % port['id'])
|
|
try:
|
|
self.nb_api.delete_lswitch(id=network_id, topic=tenant_id)
|
|
except df_exceptions.DBKeyNotFound:
|
|
LOG.debug("lswitch %s is not found in DF DB, might have "
|
|
"been deleted concurrently" % network_id)
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def update_network(self, context, network_id, network):
|
|
pnet._raise_if_updates_provider_attributes(network['network'])
|
|
with context.session.begin(subtransactions=True):
|
|
result = super(DFPlugin, self).update_network(context, network_id,
|
|
network)
|
|
if psec.PORTSECURITY in network['network']:
|
|
self._process_network_port_security_update(context,
|
|
network['network'],
|
|
result)
|
|
self._process_l3_update(context, result, network['network'])
|
|
network_version = version_db._update_db_version_row(
|
|
context.session, network_id)
|
|
|
|
self.nb_api.update_lswitch(id=network_id,
|
|
topic=result['tenant_id'],
|
|
name=result.get(
|
|
'name',
|
|
df_const.DF_NETWORK_DEFAULT_NAME),
|
|
router_external=result['router:external'],
|
|
version=network_version)
|
|
return result
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def update_port(self, context, id, port):
|
|
with context.session.begin(subtransactions=True):
|
|
parent_name, tag = self._get_data_from_binding_profile(
|
|
context, port['port'])
|
|
original_port = self.get_port(context, id)
|
|
updated_port = super(DFPlugin, self).update_port(context, id,
|
|
port)
|
|
|
|
# TODO(yuanwei): in ML2 plugin, security_groups and
|
|
# allow_address_pairs configuration depend on portsec switch is
|
|
# enabled.
|
|
port_security_value = None
|
|
if psec.PORTSECURITY in port['port']:
|
|
self._process_port_port_security_update(
|
|
context, port['port'], updated_port)
|
|
port_security_value = updated_port[psec.PORTSECURITY]
|
|
else:
|
|
original_port_security = original_port.get(psec.PORTSECURITY)
|
|
if original_port_security is not None:
|
|
updated_port[psec.PORTSECURITY] = original_port_security
|
|
port_security_value = original_port_security
|
|
else:
|
|
# if the port-security-enabled field was not set in the
|
|
# original port, we should remain this field of the
|
|
# logical port in the DF DB unchanged.
|
|
port_security_value = const.ATTR_NOT_SPECIFIED
|
|
|
|
self.update_security_group_on_port(
|
|
context, id, port, original_port, updated_port)
|
|
|
|
address_pairs_updated = False
|
|
if addr_pair.ADDRESS_PAIRS in port['port']:
|
|
address_pairs_updated = self.update_address_pairs_on_port(
|
|
context, id, port, original_port, updated_port)
|
|
if not address_pairs_updated:
|
|
updated_port[addr_pair.ADDRESS_PAIRS] = original_port.get(
|
|
addr_pair.ADDRESS_PAIRS, [])
|
|
|
|
self._update_extra_dhcp_opts_on_port(
|
|
context,
|
|
id,
|
|
port,
|
|
updated_port=updated_port)
|
|
|
|
port_version = version_db._update_db_version_row(
|
|
context.session, id)
|
|
|
|
setattr(context, 'GUARD_TRANSACTION', False)
|
|
self._process_portbindings_create_and_update(context,
|
|
port['port'],
|
|
updated_port)
|
|
|
|
ips = [ip['ip_address'] for ip in updated_port.get('fixed_ips', [])]
|
|
subnets = [ip['subnet_id'] for ip in updated_port.get('fixed_ips', [])]
|
|
|
|
chassis = None
|
|
if 'binding:host_id' in updated_port:
|
|
chassis = updated_port['binding:host_id']
|
|
|
|
# Router GW ports are not needed by dragonflow controller and
|
|
# they currently cause error as they couldnt be mapped to
|
|
# a valid ofport (or location)
|
|
if updated_port.get('device_owner') == const.DEVICE_OWNER_ROUTER_GW:
|
|
chassis = None
|
|
|
|
updated_security_groups = updated_port.get('security_groups')
|
|
if updated_security_groups == []:
|
|
security_groups = None
|
|
else:
|
|
security_groups = updated_security_groups
|
|
|
|
port_name = updated_port.get('name', df_const.DF_PORT_DEFAULT_NAME)
|
|
|
|
self.nb_api.update_lport(id=updated_port['id'],
|
|
topic=updated_port['tenant_id'],
|
|
macs=[updated_port['mac_address']],
|
|
ips=ips,
|
|
subnets=subnets,
|
|
name=port_name,
|
|
parent_name=parent_name, tag=tag,
|
|
enabled=updated_port['admin_'
|
|
'state_up'],
|
|
chassis=chassis,
|
|
device_owner=updated_port.get(
|
|
'device_owner', None),
|
|
device_id=updated_port.get(
|
|
'device_id', None),
|
|
security_groups=security_groups,
|
|
port_security_enabled=port_security_value,
|
|
allowed_address_pairs=updated_port[
|
|
addr_pair.ADDRESS_PAIRS],
|
|
binding_profile=updated_port.get(
|
|
portbindings.PROFILE, None),
|
|
binding_vnic_type=updated_port.get(
|
|
portbindings.VNIC_TYPE, None),
|
|
version=port_version)
|
|
return updated_port
|
|
|
|
def _get_data_from_binding_profile(self, context, port):
|
|
if (df_const.DF_PORT_BINDING_PROFILE not in port or
|
|
not attr.is_attr_set(
|
|
port[df_const.DF_PORT_BINDING_PROFILE])):
|
|
return None, None
|
|
parent_name = (
|
|
port[df_const.DF_PORT_BINDING_PROFILE].get('parent_name'))
|
|
tag = port[df_const.DF_PORT_BINDING_PROFILE].get('tag')
|
|
if not any((parent_name, tag)):
|
|
# An empty profile is fine.
|
|
return None, None
|
|
if not all((parent_name, tag)):
|
|
# If one is set, they both must be set.
|
|
msg = _('Invalid binding:profile. parent_name and tag are '
|
|
'both required.')
|
|
raise n_exc.InvalidInput(error_message=msg)
|
|
if not isinstance(parent_name, six.string_types):
|
|
msg = _('Invalid binding:profile. parent_name "%s" must be '
|
|
'a string.') % parent_name
|
|
raise n_exc.InvalidInput(error_message=msg)
|
|
try:
|
|
tag = int(tag)
|
|
if tag < 0 or tag > 4095:
|
|
raise ValueError
|
|
except ValueError:
|
|
msg = _('Invalid binding:profile. tag "%s" must be '
|
|
'an int between 1 and 4096, inclusive.') % tag
|
|
raise n_exc.InvalidInput(error_message=msg)
|
|
# Make sure we can successfully look up the port indicated by
|
|
# parent_name. Just let it raise the right exception if there is a
|
|
# problem.
|
|
self.get_port(context, parent_name)
|
|
return parent_name, tag
|
|
|
|
def _determine_port_security(self, context, port):
|
|
"""Returns a boolean (port_security_enabled).
|
|
|
|
Port_security is the value associated with the port if one is present
|
|
otherwise the value associated with the network is returned.
|
|
"""
|
|
if port.get('device_owner') and utils.is_port_trusted(port):
|
|
return False
|
|
|
|
if attr.is_attr_set(port.get(psec.PORTSECURITY)):
|
|
port_security_enabled = port[psec.PORTSECURITY]
|
|
else:
|
|
port_security_enabled = self._get_network_security_binding(
|
|
context, port['network_id'])
|
|
|
|
return port_security_enabled
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def create_port(self, context, port):
|
|
with context.session.begin(subtransactions=True):
|
|
parent_name, tag = self._get_data_from_binding_profile(
|
|
context, port['port'])
|
|
dhcp_opts = port['port'].get(edo_ext.EXTRADHCPOPTS, [])
|
|
db_port = super(DFPlugin, self).create_port(context, port)
|
|
# TODO(yuanwei): in ML2 plugin, security_groups and
|
|
# allow_address_pairs configuration depend on portsec switch is
|
|
# enabled.
|
|
portsec_tmp = {
|
|
psec.PORTSECURITY:
|
|
self._determine_port_security(context, port['port'])
|
|
}
|
|
self._process_port_port_security_create(context, portsec_tmp,
|
|
db_port)
|
|
|
|
sgids = self._get_security_groups_on_port(context, port)
|
|
self._process_port_create_security_group(context, db_port,
|
|
sgids)
|
|
self._process_portbindings_create_and_update(context,
|
|
port['port'],
|
|
db_port)
|
|
|
|
self._update_port_binding(db_port)
|
|
if (df_const.DF_PORT_BINDING_PROFILE in port['port'] and
|
|
attr.is_attr_set(
|
|
port['port'][df_const.DF_PORT_BINDING_PROFILE])):
|
|
db_port[df_const.DF_PORT_BINDING_PROFILE] = (
|
|
port['port'][df_const.DF_PORT_BINDING_PROFILE])
|
|
db_port[addr_pair.ADDRESS_PAIRS] = (
|
|
self._process_create_allowed_address_pairs(
|
|
context, db_port,
|
|
port['port'].get(addr_pair.ADDRESS_PAIRS)))
|
|
self._process_port_create_extra_dhcp_opts(context, db_port,
|
|
dhcp_opts)
|
|
port_version = version_db._create_db_version_row(
|
|
context.session, db_port['id'])
|
|
# This extra lookup is necessary to get the latest db model
|
|
# for the extension functions.
|
|
port_model = self._get_port(context, db_port['id'])
|
|
self._apply_dict_extend_functions('ports', db_port, port_model)
|
|
|
|
return self._create_port_in_nb_api(db_port, parent_name,
|
|
tag, port_version)
|
|
|
|
def _create_port_in_nb_api(self, port, parent_name, tag, port_version):
|
|
# The port name *must* be port['id']. It must match the iface-id set
|
|
# in the Interfaces table of the Open_vSwitch database, which nova sets
|
|
# to be the port ID.
|
|
ips = [ip['ip_address'] for ip in port.get('fixed_ips', [])]
|
|
subnets = [ip['subnet_id'] for ip in port.get('fixed_ips', [])]
|
|
|
|
chassis = None
|
|
if 'binding:host_id' in port:
|
|
chassis = port['binding:host_id']
|
|
|
|
tunnel_key = self.nb_api.allocate_tunnel_key()
|
|
|
|
# Router GW ports are not needed by dragonflow controller and
|
|
# they currently cause error as they couldnt be mapped to
|
|
# a valid ofport (or location)
|
|
if port.get('device_owner') == const.DEVICE_OWNER_ROUTER_GW:
|
|
chassis = None
|
|
|
|
security_groups = port.get('security_groups')
|
|
if security_groups == []:
|
|
sgs = None
|
|
else:
|
|
sgs = security_groups
|
|
|
|
self.nb_api.create_lport(
|
|
id=port['id'],
|
|
lswitch_id=port['network_id'],
|
|
topic=port['tenant_id'],
|
|
macs=[port['mac_address']],
|
|
ips=ips, subnets=subnets,
|
|
name=port.get('name', df_const.DF_PORT_DEFAULT_NAME),
|
|
parent_name=parent_name, tag=tag,
|
|
enabled=port.get('admin_state_up', None),
|
|
chassis=chassis, tunnel_key=tunnel_key,
|
|
version=port_version,
|
|
device_id=port.get('device_id', None),
|
|
device_owner=port.get('device_owner', None),
|
|
security_groups=sgs,
|
|
port_security_enabled=port[psec.PORTSECURITY],
|
|
binding_profile=port.get('binding:profile', None),
|
|
binding_vnic_type=port.get('binding:vnic_type', None),
|
|
allowed_address_pairs=port[addr_pair.ADDRESS_PAIRS])
|
|
|
|
return port
|
|
|
|
def _pre_delete_port(self, port, port_check):
|
|
"""Do some preliminary operations before deleting the port."""
|
|
LOG.debug("Deleting port %s", port['id'])
|
|
if not port_check:
|
|
return
|
|
|
|
if port['device_owner'] in ['network:router_interface',
|
|
'network:router_gateway',
|
|
'network:floatingip']:
|
|
fixed_ips = port['fixed_ips']
|
|
if fixed_ips:
|
|
reason = _('has device owner %s') % port['device_owner']
|
|
raise n_exc.ServicePortInUse(port_id=port['id'],
|
|
reason=reason)
|
|
else:
|
|
LOG.debug("Port %(port_id)s has owner %(port_owner)s, but "
|
|
"no IP address, so it can be deleted",
|
|
{'port_id': port['id'],
|
|
'port_owner': port['device_owner']})
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def delete_port(self, context, port_id, l3_port_check=True):
|
|
port = self.get_port(context, port_id)
|
|
self._pre_delete_port(port, l3_port_check)
|
|
topic = port['tenant_id']
|
|
|
|
with context.session.begin(subtransactions=True):
|
|
self.disassociate_floatingips(context, port_id)
|
|
super(DFPlugin, self).delete_port(context, port_id)
|
|
version_db._delete_db_version_row(context.session, port_id)
|
|
|
|
try:
|
|
self.nb_api.delete_lport(id=port_id, topic=topic)
|
|
except df_exceptions.DBKeyNotFound:
|
|
LOG.debug("port %s is not found in DF DB, might have "
|
|
"been deleted concurrently" % port_id)
|
|
|
|
def extend_port_dict_binding(self, port_res, port_db):
|
|
self._update_port_binding(port_res)
|
|
super(DFPlugin, self).extend_port_dict_binding(port_res, port_db)
|
|
|
|
def _create_router_db(self, context, router, tenant_id):
|
|
"""Create a router db object with dvr additions."""
|
|
router['distributed'] = is_distributed_router(router)
|
|
with context.session.begin(subtransactions=True):
|
|
router_db = super(
|
|
DFPlugin, self)._create_router_db(
|
|
context, router, tenant_id)
|
|
self._process_extra_attr_router_create(context, router_db, router)
|
|
return router_db
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def create_router(self, context, router):
|
|
with context.session.begin(subtransactions=True):
|
|
router = super(DFPlugin, self).create_router(
|
|
context, router)
|
|
router_version = version_db._create_db_version_row(
|
|
context.session, router['id'])
|
|
|
|
router_id = router['id']
|
|
tenant_id = router['tenant_id']
|
|
router_name = router.get('name', df_const.DF_ROUTER_DEFAULT_NAME)
|
|
self.nb_api.create_lrouter(router_id, topic=tenant_id,
|
|
name=router_name,
|
|
version=router_version,
|
|
ports=[])
|
|
return router
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def update_router(self, context, id, router):
|
|
with context.session.begin(subtransactions=True):
|
|
router = super(DFPlugin, self).update_router(
|
|
context, id, router)
|
|
router_version = version_db._update_db_version_row(
|
|
context.session, id)
|
|
|
|
tenant_id = router['tenant_id']
|
|
router_name = router.get('name', df_const.DF_ROUTER_DEFAULT_NAME)
|
|
self.nb_api.update_lrouter(id, topic=tenant_id,
|
|
name=router_name,
|
|
version=router_version)
|
|
return router
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def delete_router(self, context, router_id):
|
|
router = self.get_router(context, router_id)
|
|
with context.session.begin(subtransactions=True):
|
|
ret_val = super(DFPlugin, self).delete_router(context,
|
|
router_id)
|
|
version_db._delete_db_version_row(context.session, router_id)
|
|
try:
|
|
self.nb_api.delete_lrouter(id=router_id,
|
|
topic=router['tenant_id'])
|
|
except df_exceptions.DBKeyNotFound:
|
|
LOG.debug("router %s is not found in DF DB, might have "
|
|
"been deleted concurrently" % router_id)
|
|
return ret_val
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def add_router_interface(self, context, router_id, interface_info):
|
|
add_by_port, add_by_sub = self._validate_interface_info(
|
|
interface_info)
|
|
if add_by_sub:
|
|
subnet = self.get_subnet(context, interface_info['subnet_id'])
|
|
port = {'port': {'tenant_id': subnet['tenant_id'],
|
|
'network_id': subnet['network_id'], 'name': '',
|
|
'admin_state_up': True, 'device_id': '',
|
|
'device_owner': l3_db.DEVICE_OWNER_ROUTER_INTF,
|
|
'mac_address': attr.ATTR_NOT_SPECIFIED,
|
|
'fixed_ips': [{'subnet_id': subnet['id'],
|
|
'ip_address':
|
|
subnet['gateway_ip']}]}}
|
|
port = self.create_port(context, port)
|
|
elif add_by_port:
|
|
port = self.get_port(context, interface_info['port_id'])
|
|
subnet_id = port['fixed_ips'][0]['subnet_id']
|
|
subnet = self.get_subnet(context, subnet_id)
|
|
|
|
lswitch_id = subnet['network_id']
|
|
cidr = netaddr.IPNetwork(subnet['cidr'])
|
|
network = "%s/%s" % (port['fixed_ips'][0]['ip_address'],
|
|
str(cidr.prefixlen))
|
|
|
|
logical_port = self.nb_api.get_logical_port(port['id'],
|
|
port['tenant_id'])
|
|
|
|
interface_info['port_id'] = port['id']
|
|
if 'subnet_id' in interface_info:
|
|
del interface_info['subnet_id']
|
|
|
|
with context.session.begin(subtransactions=True):
|
|
result = super(DFPlugin, self).add_router_interface(
|
|
context, router_id, interface_info)
|
|
router_version = version_db._update_db_version_row(
|
|
context.session, router_id)
|
|
|
|
self.nb_api.add_lrouter_port(port['id'],
|
|
router_id, lswitch_id,
|
|
port['tenant_id'],
|
|
router_version=router_version,
|
|
mac=port['mac_address'],
|
|
network=network,
|
|
tunnel_key=logical_port.get_tunnel_key())
|
|
return result
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def remove_router_interface(self, context, router_id, interface_info):
|
|
with context.session.begin(subtransactions=True):
|
|
router_port_info = super(DFPlugin, self).remove_router_interface(
|
|
context, router_id, interface_info)
|
|
router_version = version_db._update_db_version_row(
|
|
context.session, router_id)
|
|
|
|
try:
|
|
self.nb_api.delete_lrouter_port(router_port_info['port_id'],
|
|
router_id,
|
|
router_port_info['tenant_id'],
|
|
router_version=router_version)
|
|
except df_exceptions.DBKeyNotFound:
|
|
LOG.debug("logical router %s is not found in DF DB, "
|
|
"suppressing delete_lrouter_port "
|
|
"exception" % router_id)
|
|
return router_port_info
|
|
|
|
def _create_dhcp_server_port(self, context, subnet):
|
|
"""Create and return dhcp port information.
|
|
|
|
If an expected failure occurs, a None port is returned.
|
|
|
|
"""
|
|
port = {'port': {'tenant_id': context.tenant_id,
|
|
'network_id': subnet['network_id'], 'name': '',
|
|
'binding:host_id': (
|
|
df_common_const.DRAGONFLOW_VIRTUAL_PORT),
|
|
'admin_state_up': True, 'device_id': '',
|
|
'device_owner': const.DEVICE_OWNER_DHCP,
|
|
'mac_address': attr.ATTR_NOT_SPECIFIED,
|
|
'fixed_ips': [{'subnet_id': subnet['id']}]}}
|
|
port = self.create_port(context, port)
|
|
|
|
return port
|
|
|
|
def _get_ports_by_subnets_and_owners(self, context,
|
|
subnet_ids, device_owners):
|
|
"""Used to get all port in a subnet by the device owner"""
|
|
LOG.debug("Dragonflow : subnet_ids: %s", subnet_ids)
|
|
filters = {'fixed_ips': {'subnet_id': subnet_ids},
|
|
'device_owner': device_owners}
|
|
return self.get_ports(context, filters=filters)
|
|
|
|
def _get_dhcp_port_for_subnet(self, context, subnet_id):
|
|
ports = self._get_ports_by_subnets_and_owners(
|
|
context,
|
|
[subnet_id],
|
|
[const.DEVICE_OWNER_DHCP])
|
|
try:
|
|
return ports[0]
|
|
except IndexError:
|
|
return None
|
|
|
|
def _get_ip_from_port(self, port):
|
|
"""Get The first Ip address from the port.
|
|
|
|
Returns the first fixed_ip address for a port
|
|
"""
|
|
if not port:
|
|
return None
|
|
for fixed_ip in port['fixed_ips']:
|
|
if "ip_address" in fixed_ip:
|
|
return fixed_ip['ip_address']
|
|
|
|
def _update_subnet_dhcp_centralized(self, context, subnet):
|
|
"""Update the dhcp configuration for the subnet
|
|
|
|
Returns the dhcp server ip address if configured
|
|
"""
|
|
if subnet['enable_dhcp']:
|
|
port = self._get_dhcp_port_for_subnet(
|
|
context,
|
|
subnet['id'])
|
|
return self._get_ip_from_port(port)
|
|
else:
|
|
return subnet['allocation_pools'][0]['start']
|
|
|
|
def _update_subnet_dhcp(self, context, old_subnet, new_subnet):
|
|
"""Update the dhcp configuration for.
|
|
|
|
Returns the dhcp ip if exists and optionaly value of dhcp server port
|
|
if this port was created.
|
|
"""
|
|
dhcp_ip = None
|
|
if cfg.CONF.df.use_centralized_ipv6_DHCP:
|
|
dhcp_ip = self._update_subnet_dhcp_centralized(context, new_subnet)
|
|
return dhcp_ip, None
|
|
|
|
if old_subnet['enable_dhcp']:
|
|
port = self._get_dhcp_port_for_subnet(
|
|
context,
|
|
old_subnet['id'])
|
|
if not new_subnet['enable_dhcp']:
|
|
if old_subnet['enable_dhcp']:
|
|
if port:
|
|
self.delete_port(context, port['id'])
|
|
return None, None
|
|
if new_subnet['enable_dhcp'] and not old_subnet['enable_dhcp']:
|
|
port = self._create_dhcp_server_port(context, new_subnet)
|
|
dhcp_ip = self._get_ip_from_port(port)
|
|
return dhcp_ip, port
|
|
if port:
|
|
dhcp_ip = self._get_ip_from_port(port)
|
|
return dhcp_ip, None
|
|
|
|
def _handle_create_subnet_dhcp(self, context, subnet):
|
|
"""Create the dhcp configuration for the subnet if required.
|
|
|
|
Returns the dhcp ip and dhcp server port (if created).
|
|
"""
|
|
if subnet['enable_dhcp']:
|
|
if cfg.CONF.df.use_centralized_ipv6_DHCP:
|
|
return subnet['allocation_pools'][0]['start'], None
|
|
else:
|
|
dhcp_port = self._create_dhcp_server_port(context, subnet)
|
|
dhcp_ip = self._get_ip_from_port(dhcp_port)
|
|
return dhcp_ip, dhcp_port
|
|
return None, None
|
|
|
|
def _get_floatingip_port(self, context, floatingip_id):
|
|
filters = {'device_id': [floatingip_id]}
|
|
floating_ports = self.get_ports(context, filters=filters)
|
|
if floating_ports:
|
|
return floating_ports[0]
|
|
return None
|
|
|
|
def _get_floatingip_subnet(self, context, subnet_id):
|
|
gateway_subnet = self.get_subnet(context, subnet_id)
|
|
if gateway_subnet['ip_version'] == 4:
|
|
return gateway_subnet
|
|
return None
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def create_floatingip(self, context, floatingip):
|
|
try:
|
|
floatingip_port = None
|
|
with context.session.begin(subtransactions=True):
|
|
floatingip_dict = super(DFPlugin, self).create_floatingip(
|
|
context, floatingip,
|
|
initial_status=const.FLOATINGIP_STATUS_ACTIVE)
|
|
|
|
floatingip_port = self._get_floatingip_port(
|
|
context, floatingip_dict['id'])
|
|
if not floatingip_port:
|
|
raise n_common_exc.DeviceNotFoundError(
|
|
device_name=floatingip_dict['id'])
|
|
subnet_id = floatingip_port['fixed_ips'][0]['subnet_id']
|
|
floatingip_subnet = self._get_floatingip_subnet(
|
|
context, subnet_id)
|
|
if floatingip_subnet is None:
|
|
raise n_exc.SubnetNotFound(subnet_id=subnet_id)
|
|
fip_version = version_db._create_db_version_row(
|
|
context.session, floatingip_dict['id'])
|
|
except Exception:
|
|
with excutils.save_and_reraise_exception() as ctxt:
|
|
ctxt.reraise = True
|
|
# delete the stale floatingip port
|
|
try:
|
|
if floatingip_port:
|
|
self.nb_api.delete_lport(floatingip_port['id'],
|
|
floatingip_port['tenant_id'])
|
|
except df_exceptions.DBKeyNotFound:
|
|
pass
|
|
|
|
self.nb_api.create_floatingip(
|
|
id=floatingip_dict['id'],
|
|
topic=floatingip_dict['tenant_id'],
|
|
name=floatingip_dict.get('name', df_const.DF_FIP_DEFAULT_NAME),
|
|
floating_ip_address=floatingip_dict['floating_ip_address'],
|
|
floating_network_id=floatingip_dict['floating_network_id'],
|
|
router_id=floatingip_dict['router_id'],
|
|
port_id=floatingip_dict['port_id'],
|
|
fixed_ip_address=floatingip_dict['fixed_ip_address'],
|
|
status=floatingip_dict['status'],
|
|
floating_port_id=floatingip_port['id'],
|
|
floating_mac_address=floatingip_port['mac_address'],
|
|
external_gateway_ip=floatingip_subnet['gateway_ip'],
|
|
version=fip_version,
|
|
external_cidr=floatingip_subnet['cidr'])
|
|
|
|
return floatingip_dict
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def update_floatingip(self, context, id, floatingip):
|
|
with context.session.begin(subtransactions=True):
|
|
floatingip_dict = super(DFPlugin, self).update_floatingip(
|
|
context, id, floatingip)
|
|
fip_version = version_db._update_db_version_row(
|
|
context.session, id)
|
|
|
|
self.nb_api.update_floatingip(
|
|
id=floatingip_dict['id'],
|
|
topic=floatingip_dict['tenant_id'],
|
|
notify=True,
|
|
name=floatingip_dict.get('name', df_const.DF_FIP_DEFAULT_NAME),
|
|
router_id=floatingip_dict['router_id'],
|
|
port_id=floatingip_dict['port_id'],
|
|
version=fip_version,
|
|
fixed_ip_address=floatingip_dict['fixed_ip_address'],
|
|
status=floatingip_dict['status'])
|
|
return floatingip_dict
|
|
|
|
@lock_db.wrap_db_lock(lock_db.RESOURCE_DF_PLUGIN)
|
|
def delete_floatingip(self, context, id):
|
|
with context.session.begin(subtransactions=True):
|
|
floatingip = self.get_floatingip(context, id)
|
|
super(DFPlugin, self).delete_floatingip(context, id)
|
|
version_db._delete_db_version_row(context.session, id)
|
|
|
|
try:
|
|
self.nb_api.delete_floatingip(id=id,
|
|
topic=floatingip['tenant_id'])
|
|
except df_exceptions.DBKeyNotFound:
|
|
LOG.debug("floatingip %s is not found in DF DB, might have "
|
|
"been deleted concurrently" % id)
|
|
|
|
def get_floatingip(self, context, id, fields=None):
|
|
with context.session.begin(subtransactions=True):
|
|
fip = super(DFPlugin, self).get_floatingip(context, id, fields)
|
|
fip['status'] = self.nb_api.get_floatingip(id).get_status()
|
|
return fip
|
|
|
|
|
|
def is_distributed_router(router):
|
|
"""Return True if router to be handled is distributed."""
|
|
try:
|
|
# See if router is a DB object first
|
|
requested_router_type = router.extra_attributes.distributed
|
|
except AttributeError:
|
|
# if not, try to see if it is a request body
|
|
requested_router_type = router.get('distributed')
|
|
if attr.is_attr_set(requested_router_type):
|
|
return requested_router_type
|
|
return cfg.CONF.router_distributed
|