Browse Source

nova-net: Kill it

Finish the job by removing all the now-unused modules. This also allows
us to - wait for it - kill mox at long last. It's a great day in the
parish.

Partial-Implements: blueprint remove-nova-network-ussuri
Partial-Implements: blueprint mox-removal-ussuri

Change-Id: Ia33ec2604b2fc2d3b6830b596cac669cc3ad6c96
changes/18/696518/18
Stephen Finucane 2 years ago
committed by Eric Fried
parent
commit
f5f73b4c4e
  1. 13
      etc/nova/rootwrap.d/api-metadata.filters
  2. 91
      etc/nova/rootwrap.d/network.filters
  3. 1
      lower-constraints.txt
  4. 2
      nova/conf/__init__.py
  5. 18
      nova/conf/netconf.py
  6. 1336
      nova/conf/network.py
  7. 18
      nova/conf/upgrade_levels.py
  8. 25
      nova/db/api.py
  9. 53
      nova/db/sqlalchemy/api.py
  10. 1
      nova/db/sqlalchemy/models.py
  11. 15
      nova/ipv6/__init__.py
  12. 55
      nova/ipv6/account_identifier.py
  13. 37
      nova/ipv6/api.py
  14. 44
      nova/ipv6/rfc2462.py
  15. 24
      nova/network/__init__.py
  16. 511
      nova/network/api.py
  17. 54
      nova/network/base_api.py
  18. 44
      nova/network/dns_driver.py
  19. 37
      nova/network/driver.py
  20. 659
      nova/network/floating_ips.py
  21. 179
      nova/network/l3.py
  22. 337
      nova/network/ldapdns.py
  23. 1632
      nova/network/linux_net.py
  24. 2165
      nova/network/manager.py
  25. 206
      nova/network/minidns.py
  26. 71
      nova/network/neutronv2/api.py
  27. 47
      nova/network/noop_dns_driver.py
  28. 369
      nova/network/rpcapi.py
  29. 1
      nova/objects/__init__.py
  30. 72
      nova/objects/dns_domain.py
  31. 4
      nova/objects/network.py
  32. 18
      nova/objects/network_request.py
  33. 282
      nova/privsep/linux_net.py
  34. 7
      nova/privsep/utils.py
  35. 69
      nova/test.py
  36. 23
      nova/tests/fixtures.py
  37. 95
      nova/tests/unit/README.rst
  38. 92
      nova/tests/unit/api/openstack/compute/test_floating_ips.py
  39. 15
      nova/tests/unit/conf_fixture.py
  40. 66
      nova/tests/unit/db/test_db_api.py
  41. 294
      nova/tests/unit/fake_network.py
  42. 570
      nova/tests/unit/network/test_api.py
  43. 26
      nova/tests/unit/network/test_l3.py
  44. 1417
      nova/tests/unit/network/test_linux_net.py
  45. 3823
      nova/tests/unit/network/test_manager.py
  46. 417
      nova/tests/unit/network/test_rpcapi.py
  47. 85
      nova/tests/unit/objects/test_dns_domain.py
  48. 2
      nova/tests/unit/objects/test_objects.py
  49. 114
      nova/tests/unit/privsep/test_linux_net.py
  50. 12
      nova/tests/unit/privsep/test_utils.py
  51. 42
      nova/tests/unit/test_fixtures.py
  52. 276
      nova/tests/unit/test_iptables_network.py
  53. 82
      nova/tests/unit/test_ipv6.py
  54. 5
      nova/tests/unit/test_profiler.py
  55. 24
      nova/tests/unit/utils.py
  56. 13
      nova/utils.py
  57. 72
      releasenotes/notes/remove-nova-network-c02953ba72a1795d.yaml
  58. 11
      setup.cfg
  59. 1
      test-requirements.txt

13
etc/nova/rootwrap.d/api-metadata.filters

@ -1,13 +0,0 @@
# nova-rootwrap command filters for api-metadata nodes
# This is needed on nova-api hosts running with "metadata" in enabled_apis
# or when running nova-api-metadata
# This file should be owned by (and only-writeable by) the root user
[Filters]
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
iptables-save: CommandFilter, iptables-save, root
ip6tables-save: CommandFilter, ip6tables-save, root
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
iptables-restore: CommandFilter, iptables-restore, root
ip6tables-restore: CommandFilter, ip6tables-restore, root

91
etc/nova/rootwrap.d/network.filters

@ -1,91 +0,0 @@
# nova-rootwrap command filters for network nodes
# This file should be owned by (and only-writeable by) the root user
[Filters]
# nova/virt/libvirt/vif.py: 'ip', 'tuntap', 'add', dev, 'mode', 'tap'
# nova/virt/libvirt/vif.py: 'ip', 'link', 'set', dev, 'up'
# nova/virt/libvirt/vif.py: 'ip', 'link', 'delete', dev
# nova/network/linux_net.py: 'ip', 'addr', 'add', str(floating_ip)+'/32'i..
# nova/network/linux_net.py: 'ip', 'addr', 'del', str(floating_ip)+'/32'..
# nova/network/linux_net.py: 'ip', 'addr', 'add', '169.254.169.254/32',..
# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', dev, 'scope',..
# nova/network/linux_net.py: 'ip', 'addr', 'del/add', ip_params, dev)
# nova/network/linux_net.py: 'ip', 'addr', 'del', params, fields[-1]
# nova/network/linux_net.py: 'ip', 'addr', 'add', params, bridge
# nova/network/linux_net.py: 'ip', '-f', 'inet6', 'addr', 'change', ..
# nova/network/linux_net.py: 'ip', 'link', 'set', 'dev', dev, 'promisc',..
# nova/network/linux_net.py: 'ip', 'link', 'add', 'link', bridge_if ...
# nova/network/linux_net.py: 'ip', 'link', 'set', interface, address,..
# nova/network/linux_net.py: 'ip', 'link', 'set', interface, 'up'
# nova/network/linux_net.py: 'ip', 'link', 'set', bridge, 'up'
# nova/network/linux_net.py: 'ip', 'addr', 'show', 'dev', interface, ..
# nova/network/linux_net.py: 'ip', 'link', 'set', dev, address, ..
# nova/network/linux_net.py: 'ip', 'link', 'set', dev, 'up'
# nova/network/linux_net.py: 'ip', 'route', 'add', ..
# nova/network/linux_net.py: 'ip', 'route', 'del', .
# nova/network/linux_net.py: 'ip', 'route', 'show', 'dev', dev
ip: CommandFilter, ip, root
# nova/virt/libvirt/vif.py: 'ovs-vsctl', ...
# nova/virt/libvirt/vif.py: 'ovs-vsctl', 'del-port', ...
# nova/network/linux_net.py: 'ovs-vsctl', ....
ovs-vsctl: CommandFilter, ovs-vsctl, root
# nova/network/linux_net.py: 'ovs-ofctl', ....
ovs-ofctl: CommandFilter, ovs-ofctl, root
# nova/virt/libvirt/vif.py: 'ivs-ctl', ...
# nova/virt/libvirt/vif.py: 'ivs-ctl', 'del-port', ...
# nova/network/linux_net.py: 'ivs-ctl', ....
ivs-ctl: CommandFilter, ivs-ctl, root
# nova/virt/libvirt/vif.py: 'ifc_ctl', ...
ifc_ctl: CommandFilter, /opt/pg/bin/ifc_ctl, root
# nova/network/linux_net.py: 'ebtables', '-D' ...
# nova/network/linux_net.py: 'ebtables', '-I' ...
ebtables: CommandFilter, ebtables, root
ebtables_usr: CommandFilter, ebtables, root
# nova/network/linux_net.py: 'ip[6]tables-save' % (cmd, '-t', ...
iptables-save: CommandFilter, iptables-save, root
ip6tables-save: CommandFilter, ip6tables-save, root
# nova/network/linux_net.py: 'ip[6]tables-restore' % (cmd,)
iptables-restore: CommandFilter, iptables-restore, root
ip6tables-restore: CommandFilter, ip6tables-restore, root
# nova/network/linux_net.py: 'arping', '-U', floating_ip, '-A', '-I', ...
# nova/network/linux_net.py: 'arping', '-U', network_ref['dhcp_server'],..
arping: CommandFilter, arping, root
# nova/network/linux_net.py: 'dhcp_release', dev, address, mac_address
dhcp_release: CommandFilter, dhcp_release, root
# nova/network/linux_net.py: 'kill', '-9', pid
# nova/network/linux_net.py: 'kill', '-HUP', pid
kill_dnsmasq: KillFilter, root, /usr/sbin/dnsmasq, -9, -HUP
# nova/network/linux_net.py: 'kill', pid
kill_radvd: KillFilter, root, /usr/sbin/radvd
# nova/network/linux_net.py: dnsmasq call
dnsmasq: EnvFilter, env, root, CONFIG_FILE=, NETWORK_ID=, dnsmasq
# nova/network/linux_net.py: 'radvd', '-C', '%s' % _ra_file(dev, 'conf'..
radvd: CommandFilter, radvd, root
# nova/network/linux_net.py: 'brctl', 'addbr', bridge
# nova/network/linux_net.py: 'brctl', 'setfd', bridge, 0
# nova/network/linux_net.py: 'brctl', 'stp', bridge, 'off'
# nova/network/linux_net.py: 'brctl', 'addif', bridge, interface
brctl: CommandFilter, brctl, root
# nova/network/linux_net.py: 'sysctl', ....
sysctl: CommandFilter, sysctl, root
# nova/network/linux_net.py: 'conntrack'
conntrack: CommandFilter, conntrack, root
# nova/network/linux_net.py: 'fp-vdev'
fp-vdev: CommandFilter, fp-vdev, root

1
lower-constraints.txt

@ -54,7 +54,6 @@ mccabe==0.2.1
microversion-parse==0.2.1
mock==3.0.0
monotonic==1.4
mox3==0.20.0
msgpack==0.5.6
msgpack-python==0.5.6
munch==2.2.0

2
nova/conf/__init__.py

@ -42,7 +42,6 @@ from nova.conf import keystone
from nova.conf import libvirt
from nova.conf import mks
from nova.conf import netconf
from nova.conf import network
from nova.conf import neutron
from nova.conf import notifications
from nova.conf import novnc
@ -94,7 +93,6 @@ key_manager.register_opts(CONF)
keystone.register_opts(CONF)
libvirt.register_opts(CONF)
netconf.register_opts(CONF)
network.register_opts(CONF)
neutron.register_opts(CONF)
notifications.register_opts(CONF)
novnc.register_opts(CONF)

18
nova/conf/netconf.py

@ -34,10 +34,7 @@ Possible values:
Related options:
* metadata_host
* my_block_storage_ip
* routing_source_ip
* vpn_ip
"""),
cfg.StrOpt("my_block_storage_ip",
default="$my_ip",
@ -70,6 +67,21 @@ Must be valid within AMQP key.
Possible values:
* String with hostname, FQDN or IP address. Default is hostname of this host.
"""),
# TODO(sfinucan): This option is tied into the XenAPI, VMWare and Libvirt
# drivers.
# We should remove this dependency by either adding a new opt for each
# driver or simply removing the offending code. Until then we cannot
# deprecate this option.
cfg.BoolOpt("flat_injected",
default=False,
help="""
This option determines whether the network setup information is injected into
the VM before it is booted. While it was originally designed to be used only
by nova-network, it is also used by the vmware and xenapi virt drivers to
control whether network information is injected into a VM. The libvirt virt
driver also uses it when we use config_drive to configure network to control
whether network information is injected into a VM.
"""),
]

1336
nova/conf/network.py
File diff suppressed because it is too large
View File

18
nova/conf/upgrade_levels.py

@ -96,24 +96,6 @@ Conductor RPC API version cap.
Possible values:
* By default send the latest version the client knows about
* A string representing a version number in the format 'N.N';
for example, possible values might be '1.12' or '2.0'.
* An OpenStack release name, in lower case, such as 'mitaka' or
'liberty'.
"""),
cfg.StrOpt('network',
deprecated_for_removal=True,
deprecated_since='18.0.0',
deprecated_reason="""
The nova-network service was deprecated in 14.0.0 (Newton) and will be
removed in an upcoming release.
""",
help="""
Network RPC API version cap.
Possible values:
* By default send the latest version the client knows about
* A string representing a version number in the format 'N.N';
for example, possible values might be '1.12' or '2.0'.

25
nova/db/api.py

@ -499,31 +499,6 @@ def floating_ip_update(context, address, values):
return IMPL.floating_ip_update(context, address, values)
def dnsdomain_get_all(context):
"""Get a list of all dnsdomains in our database."""
return IMPL.dnsdomain_get_all(context)
def dnsdomain_register_for_zone(context, fqdomain, zone):
"""Associated a DNS domain with an availability zone."""
return IMPL.dnsdomain_register_for_zone(context, fqdomain, zone)
def dnsdomain_register_for_project(context, fqdomain, project):
"""Associated a DNS domain with a project id."""
return IMPL.dnsdomain_register_for_project(context, fqdomain, project)
def dnsdomain_unregister(context, fqdomain):
"""Purge associations for the specified DNS zone."""
return IMPL.dnsdomain_unregister(context, fqdomain)
def dnsdomain_get(context, fqdomain):
"""Get the db record for the specified domain."""
return IMPL.dnsdomain_get(context, fqdomain)
####################

53
nova/db/sqlalchemy/api.py

@ -1182,58 +1182,6 @@ def floating_ip_update(context, address, values):
###################
@require_context
@pick_context_manager_reader
def dnsdomain_get(context, fqdomain):
return model_query(context, models.DNSDomain, read_deleted="no").\
filter_by(domain=fqdomain).\
with_for_update().\
first()
def _dnsdomain_get_or_create(context, fqdomain):
domain_ref = dnsdomain_get(context, fqdomain)
if not domain_ref:
dns_ref = models.DNSDomain()
dns_ref.update({'domain': fqdomain,
'availability_zone': None,
'project_id': None})
return dns_ref
return domain_ref
@pick_context_manager_writer
def dnsdomain_register_for_zone(context, fqdomain, zone):
domain_ref = _dnsdomain_get_or_create(context, fqdomain)
domain_ref.scope = 'private'
domain_ref.availability_zone = zone
context.session.add(domain_ref)
@pick_context_manager_writer
def dnsdomain_register_for_project(context, fqdomain, project):
domain_ref = _dnsdomain_get_or_create(context, fqdomain)
domain_ref.scope = 'public'
domain_ref.project_id = project
context.session.add(domain_ref)
@pick_context_manager_writer
def dnsdomain_unregister(context, fqdomain):
model_query(context, models.DNSDomain).\
filter_by(domain=fqdomain).\
delete()
@pick_context_manager_reader
def dnsdomain_get_all(context):
return model_query(context, models.DNSDomain, read_deleted="no").all()
###################
@oslo_db_api.wrap_db_retry(max_retries=5, retry_on_deadlock=True)
@pick_context_manager_writer
def fixed_ip_associate(context, address, instance_uuid, network_id=None,
@ -5329,6 +5277,7 @@ def _archive_deleted_rows_for_table(metadata, tablename, max_rows, before):
# No corresponding shadow table; skip it.
return rows_archived, deleted_instance_uuids
# TODO(stephenfin): Drop this when we drop the table
if tablename == "dns_domains":
# We have one table (dns_domains) where the key is called
# "domain" rather than "id"

1
nova/db/sqlalchemy/models.py

@ -971,6 +971,7 @@ class FloatingIp(BASE, NovaBase, models.SoftDeleteMixin):
'FixedIp.deleted == 0)')
# TODO(stephenfin): Remove in V or later
class DNSDomain(BASE, NovaBase, models.SoftDeleteMixin):
"""Represents a DNS domain with availability zone or project info."""
__tablename__ = 'dns_domains'

15
nova/ipv6/__init__.py

@ -1,15 +0,0 @@
# Copyright (c) 2011 OpenStack Foundation
#
# 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 nova.ipv6.api import * # noqa

55
nova/ipv6/account_identifier.py

@ -1,55 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Justin Santa Barbara
# 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.
"""IPv6 address generation with account identifier embedded."""
import hashlib
import netaddr
import six
from nova.i18n import _
def to_global(prefix, mac, project_id):
addr = project_id
if isinstance(addr, six.text_type):
addr = addr.encode('utf-8')
addr = hashlib.sha1(addr)
addr = int(addr.hexdigest()[:8], 16) << 32
project_hash = netaddr.IPAddress(addr)
static_num = netaddr.IPAddress(0xff << 24)
try:
mac_suffix = netaddr.EUI(mac).value & 0xffffff
mac_addr = netaddr.IPAddress(mac_suffix)
except netaddr.AddrFormatError:
raise TypeError(_('Bad mac for to_global_ipv6: %s') % mac)
try:
maskIP = netaddr.IPNetwork(prefix).ip
return (project_hash ^ static_num ^ mac_addr | maskIP).format()
except netaddr.AddrFormatError:
raise TypeError(_('Bad prefix for to_global_ipv6: %s') % prefix)
def to_mac(ipv6_address):
address = netaddr.IPAddress(ipv6_address)
mask1 = netaddr.IPAddress('::ff:ffff')
mac = netaddr.EUI(int(address & mask1)).words
return ':'.join(['02', '16', '3e'] + ['%02x' % i for i in mac[3:6]])

37
nova/ipv6/api.py

@ -1,37 +0,0 @@
# Copyright (c) 2011 OpenStack Foundation
#
# 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 stevedore import driver
import nova.conf
CONF = nova.conf.CONF
IMPL = None
def reset_backend():
global IMPL
IMPL = driver.DriverManager("nova.ipv6_backend",
CONF.ipv6_backend).driver
def to_global(prefix, mac, project_id):
return IMPL.to_global(prefix, mac, project_id)
def to_mac(ipv6_address):
return IMPL.to_mac(ipv6_address)
reset_backend()

44
nova/ipv6/rfc2462.py

@ -1,44 +0,0 @@
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2011 Justin Santa Barbara
# 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.
"""RFC2462 style IPv6 address generation."""
import netaddr
from nova.i18n import _
def to_global(prefix, mac, project_id):
try:
mac64 = netaddr.EUI(mac).modified_eui64().value
mac64_addr = netaddr.IPAddress(mac64)
except netaddr.AddrFormatError:
raise TypeError(_('Bad mac for to_global_ipv6: %s') % mac)
try:
maskIP = netaddr.IPNetwork(prefix).ip
return (mac64_addr | maskIP).format()
except netaddr.AddrFormatError:
raise TypeError(_('Bad prefix for to_global_ipv6: %s') % prefix)
def to_mac(ipv6_address):
address = netaddr.IPAddress(ipv6_address)
mask1 = netaddr.IPAddress('::ffff:ffff:ffff:ffff')
mask2 = netaddr.IPAddress('::0200:0:0:0')
mac64 = netaddr.EUI(int(address & mask1 ^ mask2)).words
return ':'.join(['%02x' % i for i in mac64[0:3] + mac64[5:8]])

24
nova/network/__init__.py

@ -16,28 +16,8 @@
from oslo_utils import importutils
import nova.conf
NOVA_NET_API = 'nova.network.api.API'
NEUTRON_NET_API = 'nova.network.neutronv2.api.API'
CONF = nova.conf.CONF
def is_neutron():
"""Does this configuration mean we're neutron.
This logic exists as a separate config option
"""
return CONF.use_neutron
# TODO(stephenfin): Remove this layer of indirection
def API():
if is_neutron():
network_api_class = NEUTRON_NET_API
else:
network_api_class = NOVA_NET_API
cls = importutils.import_class(network_api_class)
cls = importutils.import_class('nova.network.neutronv2.api.API')
return cls()

511
nova/network/api.py

@ -1,511 +0,0 @@
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# Copyright 2013 IBM Corp.
# 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 oslo_config import cfg
from oslo_log import log as logging
from oslo_utils import strutils
from nova import exception
from nova.network import base_api
from nova.network import floating_ips
from nova.network import model as network_model
from nova.network import rpcapi as network_rpcapi
from nova import objects
from nova.objects import base as obj_base
from nova import profiler
from nova import utils
CONF = cfg.CONF
LOG = logging.getLogger(__name__)
@profiler.trace_cls("network_api")
class API(base_api.NetworkAPI):
"""API for doing networking via the nova-network network manager.
This is a pluggable module - other implementations do networking via
other services (such as Neutron).
"""
def __init__(self, **kwargs):
self.network_rpcapi = network_rpcapi.NetworkAPI()
helper = utils.ExceptionHelper
# NOTE(vish): this local version of floating_manager has to convert
# ClientExceptions back since they aren't going over rpc.
self.floating_manager = helper(floating_ips.LocalManager())
super(API, self).__init__(**kwargs)
def get_all(self, context):
"""Get all the networks.
If it is an admin user then api will return all the
networks. If it is a normal user and nova Flat or FlatDHCP
networking is being used then api will return all
networks. Otherwise api will only return the networks which
belong to the user's project.
"""
if "nova.network.manager.Flat" in CONF.network_manager:
project_only = "allow_none"
else:
project_only = True
try:
return objects.NetworkList.get_all(context,
project_only=project_only)
except exception.NoNetworksFound:
return []
def get(self, context, network_uuid):
return objects.Network.get_by_uuid(context, network_uuid)
def create(self, context, **kwargs):
return self.network_rpcapi.create_networks(context, **kwargs)
def delete(self, context, network_uuid):
network = self.get(context, network_uuid)
if network.project_id is not None:
raise exception.NetworkInUse(network_id=network_uuid)
return self.network_rpcapi.delete_network(context, network_uuid, None)
def get_fixed_ip(self, context, id):
return objects.FixedIP.get_by_id(context, id)
def get_fixed_ip_by_address(self, context, address):
return objects.FixedIP.get_by_address(context, address)
def get_floating_ip(self, context, id):
if not strutils.is_int_like(id):
raise exception.InvalidID(id=id)
return objects.FloatingIP.get_by_id(context, id)
def get_floating_ip_pools(self, context):
return objects.FloatingIP.get_pool_names(context)
def get_floating_ip_by_address(self, context, address):
return objects.FloatingIP.get_by_address(context, address)
def get_floating_ips_by_project(self, context):
return objects.FloatingIPList.get_by_project(context,
context.project_id)
def get_instance_id_by_floating_address(self, context, address):
fixed_ip = objects.FixedIP.get_by_floating_address(context, address)
if fixed_ip is None:
return None
else:
return fixed_ip.instance_uuid
def get_vifs_by_instance(self, context, instance):
vifs = objects.VirtualInterfaceList.get_by_instance_uuid(context,
instance.uuid)
for vif in vifs:
if vif.network_id is not None:
network = objects.Network.get_by_id(context, vif.network_id,
project_only='allow_none')
vif.net_uuid = network.uuid
return vifs
def get_vif_by_mac_address(self, context, mac_address):
vif = objects.VirtualInterface.get_by_address(context,
mac_address)
if vif.network_id is not None:
network = objects.Network.get_by_id(context, vif.network_id,
project_only='allow_none')
vif.net_uuid = network.uuid
return vif
def allocate_floating_ip(self, context, pool=None):
"""Adds (allocates) a floating IP to a project from a pool."""
return self.floating_manager.allocate_floating_ip(context,
context.project_id, False, pool)
def release_floating_ip(self, context, address,
affect_auto_assigned=False):
"""Removes (deallocates) a floating IP with address from a project."""
return self.floating_manager.deallocate_floating_ip(context, address,
affect_auto_assigned)
def disassociate_and_release_floating_ip(self, context, instance,
floating_ip):
"""Removes (deallocates) and deletes the floating IP.
This api call was added to allow this to be done in one operation
if using neutron.
"""
address = floating_ip['address']
if floating_ip.get('fixed_ip_id'):
try:
self.disassociate_floating_ip(context, instance, address)
except exception.FloatingIpNotAssociated:
msg = ("Floating IP %s has already been disassociated, "
"perhaps by another concurrent action.") % address
LOG.debug(msg)
# release ip from project
return self.release_floating_ip(context, address)
@base_api.refresh_cache
def associate_floating_ip(self, context, instance,
floating_address, fixed_address,
affect_auto_assigned=False):
"""Associates a floating IP with a fixed IP.
Ensures floating IP is allocated to the project in context.
Does not verify ownership of the fixed IP. Caller is assumed to have
checked that the instance is properly owned.
"""
orig_instance_uuid = self.floating_manager.associate_floating_ip(
context, floating_address, fixed_address, affect_auto_assigned)
if orig_instance_uuid:
msg_dict = dict(address=floating_address,
instance_id=orig_instance_uuid)
LOG.info('re-assign floating IP %(address)s from '
'instance %(instance_id)s', msg_dict)
orig_instance = objects.Instance.get_by_uuid(
context, orig_instance_uuid, expected_attrs=['flavor'])
# purge cached nw info for the original instance
base_api.update_instance_cache_with_nw_info(self, context,
orig_instance)
@base_api.refresh_cache
def disassociate_floating_ip(self, context, instance, address,
affect_auto_assigned=False):
"""Disassociates a floating IP from fixed IP it is associated with."""
return self.floating_manager.disassociate_floating_ip(context, address,
affect_auto_assigned)
@staticmethod
def _requested_nets_as_obj_list(requested_networks):
"""Helper method to convert a list of requested network tuples into an
objects.NetworkRequestList.
:param requested_networks: List of requested networks.
:return: objects.NetworkRequestList instance
"""
if requested_networks and not isinstance(requested_networks,
objects.NetworkRequestList):
requested_networks = objects.NetworkRequestList.from_tuples(
requested_networks)
return requested_networks
@base_api.refresh_cache
def allocate_for_instance(self, context, instance, vpn,
requested_networks,
security_groups=None,
bind_host_id=None, attach=False,
resource_provider_mapping=None):
"""Allocates all network structures for an instance.
:param context: The request context.
:param instance: nova.objects.instance.Instance object.
:param vpn: A boolean, if True, indicate a vpn to access the instance.
:param requested_networks: A list of requested_network tuples
containing network_id and fixed_ip
:param security_groups: None or security groups to allocate for
instance.
:param bind_host_id: ignored by this driver.
:param attach: ignored by this driver
:param resource_provider_mapping: ignored by this driver
:returns: network info as from get_instance_nw_info() below
"""
# NOTE(vish): We can't do the floating ip allocation here because
# this is called from compute.manager which shouldn't
# have db access so we do it on the other side of the
# rpc.
flavor = instance.get_flavor()
args = {}
args['vpn'] = vpn
args['requested_networks'] = requested_networks
args['instance_id'] = instance.uuid
args['project_id'] = instance.project_id
args['host'] = instance.host
args['rxtx_factor'] = flavor['rxtx_factor']
# Check to see if we're asked to 'auto' allocate networks because if
# so we need to just null out the requested_networks value so the
# network manager doesn't try to get networks with uuid 'auto' which
# doesn't exist.
if requested_networks:
requested_networks = self._requested_nets_as_obj_list(
requested_networks)
if requested_networks.auto_allocate:
args['requested_networks'] = None
nw_info = self.network_rpcapi.allocate_for_instance(context, **args)
nw_info = network_model.NetworkInfo.hydrate(nw_info)
# check to see if nothing was allocated and we were requested to
# auto-allocate
if (not nw_info and requested_networks and
requested_networks.auto_allocate):
raise exception.UnableToAutoAllocateNetwork(
project_id=instance.project_id)
return nw_info
def deallocate_for_instance(self, context, instance,
requested_networks=None):
"""Deallocates all network structures related to instance."""
# NOTE(vish): We can't do the floating ip deallocation here because
# this is called from compute.manager which shouldn't
# have db access so we do it on the other side of the
# rpc.
if not isinstance(instance, obj_base.NovaObject):
instance = objects.Instance._from_db_object(context,
objects.Instance(), instance)
# In the case of 'auto' allocation for networks, just pass None for
# requested_networks since 'auto' isn't an actual network.
requested_networks = self._requested_nets_as_obj_list(
requested_networks)
if requested_networks and requested_networks.auto_allocate:
requested_networks = None
self.network_rpcapi.deallocate_for_instance(context, instance=instance,
requested_networks=requested_networks)
# NOTE(danms): Here for neutron compatibility
def allocate_port_for_instance(self, context, instance, port_id,
network_id=None, requested_ip=None,
bind_host_id=None, tag=None):
raise NotImplementedError()
# NOTE(danms): Here for neutron compatibility
def deallocate_port_for_instance(self, context, instance, port_id):
raise NotImplementedError()
# NOTE(danms): Here for neutron compatibility
def list_ports(self, *args, **kwargs):
raise NotImplementedError()
# NOTE(danms): Here for neutron compatibility
def show_port(self, *args, **kwargs):
raise NotImplementedError()
@base_api.refresh_cache
def add_fixed_ip_to_instance(self, context, instance, network_id):
"""Adds a fixed IP to instance from specified network."""
flavor = instance.get_flavor()
args = {'instance_id': instance.uuid,
'rxtx_factor': flavor['rxtx_factor'],
'host': instance.host,
'network_id': network_id}
nw_info = self.network_rpcapi.add_fixed_ip_to_instance(
context, **args)
return network_model.NetworkInfo.hydrate(nw_info)
@base_api.refresh_cache
def remove_fixed_ip_from_instance(self, context, instance, address):
"""Removes a fixed IP from instance from specified network."""
flavor = instance.get_flavor()
args = {'instance_id': instance.uuid,
'rxtx_factor': flavor['rxtx_factor'],
'host': instance.host,
'address': address}
nw_info = self.network_rpcapi.remove_fixed_ip_from_instance(
context, **args)
return network_model.NetworkInfo.hydrate(nw_info)
def _get_instance_nw_info(self, context, instance, **kwargs):
"""Returns all network info related to an instance."""
flavor = instance.get_flavor()
args = {'instance_id': instance.uuid,
'rxtx_factor': flavor['rxtx_factor'],
'host': instance.host,
'project_id': instance.project_id}
nw_info = self.network_rpcapi.get_instance_nw_info(context, **args)
return network_model.NetworkInfo.hydrate(nw_info)
def validate_networks(self, context, requested_networks, num_instances):
"""validate the networks passed at the time of creating
the server.
Return the number of instances that can be successfully allocated
with the requested network configuration.
"""
if requested_networks:
self.network_rpcapi.validate_networks(context,
requested_networks)
# Neutron validation checks and returns how many of num_instances
# instances can be supported by the quota. For Nova network
# this is part of the subsequent quota check, so we just return
# the requested number in this case.
return num_instances
def create_resource_requests(
self, context, requested_networks, pci_requests=None,
affinity_policy=None):
"""Retrieve all information for the networks passed at the time of
creating the server.
:param context: The request context.
:param requested_networks: The networks requested for the server.
:type requested_networks: nova.objects.NetworkRequestList
:param pci_requests: The list of PCI requests to which additional PCI
requests created here will be added.
:type pci_requests: nova.objects.InstancePCIRequests
:param affinity_policy: requested pci numa affinity policy
:type affinity_policy: nova.objects.fields.PCINUMAAffinityPolicy
:returns: A tuple with an instance of ``objects.NetworkMetadata`` for
use by the scheduler or None and a list of RequestGroup
objects representing the resource needs of each requested
port
"""
# This is NOOP for Nova network since it doesn't support SR-IOV or
# NUMA-aware vSwitch functionality.
return None, []
def get_dns_domains(self, context):
"""Returns a list of available dns domains.
These can be used to create DNS entries for floating IPs.
"""
return self.network_rpcapi.get_dns_domains(context)
def add_dns_entry(self, context, address, name, dns_type, domain):
"""Create specified DNS entry for address."""
args = {'address': address,
'name': name,
'dns_type': dns_type,
'domain': domain}
return self.network_rpcapi.add_dns_entry(context, **args)
def modify_dns_entry(self, context, name, address, domain):
"""Create specified DNS entry for address."""
args = {'address': address,
'name': name,
'domain': domain}
return self.network_rpcapi.modify_dns_entry(context, **args)
def delete_dns_entry(self, context, name, domain):
"""Delete the specified dns entry."""
args = {'name': name, 'domain': domain}
return self.network_rpcapi.delete_dns_entry(context, **args)
def delete_dns_domain(self, context, domain):
"""Delete the specified dns domain."""
return self.network_rpcapi.delete_dns_domain(context, domain=domain)
def get_dns_entries_by_address(self, context, address, domain):
"""Get entries for address and domain."""
args = {'address': address, 'domain': domain}
return self.network_rpcapi.get_dns_entries_by_address(context, **args)
def get_dns_entries_by_name(self, context, name, domain):
"""Get entries for name and domain."""
args = {'name': name, 'domain': domain}
return self.network_rpcapi.get_dns_entries_by_name(context, **args)
def create_private_dns_domain(self, context, domain, availability_zone):
"""Create a private DNS domain with nova availability zone."""
args = {'domain': domain, 'av_zone': availability_zone}
return self.network_rpcapi.create_private_dns_domain(context, **args)
def create_public_dns_domain(self, context, domain, project=None):
"""Create a public DNS domain with optional nova project."""
args = {'domain': domain, 'project': project}
return self.network_rpcapi.create_public_dns_domain(context, **args)
def setup_networks_on_host(self, context, instance, host=None,
teardown=False):
"""Setup or teardown the network structures on hosts related to
instance.
"""
host = host or instance.host
# NOTE(tr3buchet): host is passed in cases where we need to setup
# or teardown the networks on a host which has been migrated to/from
# and instance.host is not yet or is no longer equal to
args = {'instance_id': instance.id,
'host': host,
'teardown': teardown,
'instance': instance}
self.network_rpcapi.setup_networks_on_host(context, **args)
def _get_multi_addresses(self, context, instance):
try:
fixed_ips = objects.FixedIPList.get_by_instance_uuid(
context, instance.uuid)
except exception.FixedIpNotFoundForInstance:
return False, []
addresses = []
for fixed in fixed_ips:
for floating in fixed.floating_ips:
addresses.append(floating.address)
return fixed_ips[0].network.multi_host, addresses
def migrate_instance_start(self, context, instance, migration):
"""Start to migrate the network of an instance."""
flavor = instance.get_flavor()
args = dict(
instance_uuid=instance.uuid,
rxtx_factor=flavor['rxtx_factor'],
project_id=instance.project_id,
source_compute=migration['source_compute'],
dest_compute=migration['dest_compute'],
floating_addresses=None,
)
multi_host, addresses = self._get_multi_addresses(context, instance)
if multi_host:
args['floating_addresses'] = addresses
args['host'] = migration['source_compute']
self.network_rpcapi.migrate_instance_start(context, **args)
def migrate_instance_finish(
self, context, instance, migration, provider_mappings):
"""Finish migrating the network of an instance."""
flavor = instance.get_flavor()
args = dict(
instance_uuid=instance.uuid,
rxtx_factor=flavor['rxtx_factor'],
project_id=instance.project_id,
source_compute=migration.source_compute,
dest_compute=migration.dest_compute,
floating_addresses=None,
)
multi_host, addresses = self._get_multi_addresses(context, instance)
if multi_host:
args['floating_addresses'] = addresses
args['host'] = migration.dest_compute
self.network_rpcapi.migrate_instance_finish(context, **args)
def setup_instance_network_on_host(
self, context, instance, host, migration=None,
provider_mappings=None):
"""Setup network for specified instance on host."""
self.migrate_instance_finish(
context, instance, {'source_compute': None, 'dest_compute': host},
None)
def cleanup_instance_network_on_host(self, context, instance, host):
"""Cleanup network for specified instance on host."""
self.migrate_instance_start(context, instance,
{'source_compute': host,
'dest_compute': None})

54
nova/network/base_api.py

@ -102,18 +102,6 @@ class NetworkAPI(base.Base):
"""Get specific network for client."""
raise NotImplementedError()
def create(self, context, **kwargs):
"""Create a network."""
raise NotImplementedError()
def delete(self, context, network_uuid):
"""Delete a specific network."""
raise NotImplementedError()
def get_fixed_ip(self, context, id):
"""Get fixed IP by id."""
raise NotImplementedError()
def get_fixed_ip_by_address(self, context, address):
"""Get fixed IP by address."""
raise NotImplementedError()
@ -149,10 +137,6 @@ class NetworkAPI(base.Base):
"""
raise NotImplementedError()
def get_vif_by_mac_address(self, context, mac_address):
"""Get vif mac address."""
raise NotImplementedError()
def allocate_floating_ip(self, context, pool=None):
"""Adds (allocate) floating IP to a project from a pool."""
raise NotImplementedError()
@ -281,44 +265,6 @@ class NetworkAPI(base.Base):
"""
raise NotImplementedError()
def get_dns_domains(self, context):
"""Returns a list of available dns domains.
These can be used to create DNS entries for floating IPs.
"""
raise NotImplementedError()
def add_dns_entry(self, context, address, name, dns_type, domain):
"""Create specified DNS entry for address."""
raise NotImplementedError()
def modify_dns_entry(self, context, name, address, domain):
"""Create specified DNS entry for address."""
raise NotImplementedError()
def delete_dns_entry(self, context, name, domain):
"""Delete the specified dns entry."""
raise NotImplementedError()
def delete_dns_domain(self, context, domain):
"""Delete the specified dns domain."""
raise NotImplementedError()
def get_dns_entries_by_address(self, context, address, domain):
"""Get entries for address and domain."""
raise NotImplementedError()
def get_dns_entries_by_name(self, context, name, domain):
"""Get entries for name and domain."""
raise NotImplementedError()
def create_private_dns_domain(self, context, domain, availability_zone):
"""Create a private DNS domain with nova availability zone."""
raise NotImplementedError()
def create_public_dns_domain(self, context, domain, project=None):
"""Create a public DNS domain with optional nova project."""
raise NotImplementedError()
def setup_networks_on_host(self, context, instance, host=None,
teardown=False):
"""Setup or teardown the network structures on hosts related to

44
nova/network/dns_driver.py

@ -1,44 +0,0 @@
# Copyright 2011 Andrew Bogott for the Wikimedia Foundation
#
# 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.
class DNSDriver(object):
"""Defines the DNS manager interface. Does nothing."""
def __init__(self):
pass
def get_domains(self):
raise NotImplementedError()
def create_entry(self, _name, _address, _type, _domain):
raise NotImplementedError()
def delete_entry(self, _name, _domain):
raise NotImplementedError()
def modify_address(self, _name, _address, _domain):
raise NotImplementedError()
def get_entries_by_address(self, _address, _domain):
raise NotImplementedError()
def get_entries_by_name(self, _name, _domain):
raise NotImplementedError()
def create_domain(self, _fqdomain):
raise NotImplementedError()
def delete_domain(self, _fqdomain):
raise NotImplementedError()

37
nova/network/driver.py

@ -1,37 +0,0 @@
# Copyright 2012 Red Hat, Inc.
#
# 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 sys
from oslo_log import log as logging
from oslo_utils import importutils
import nova.conf
CONF = nova.conf.CONF
LOG = logging.getLogger(__name__)
def load_network_driver(network_driver=None):
if not network_driver:
network_driver = CONF.network_driver
if not network_driver:
LOG.error("Network driver option required, but not specified")
sys.exit(1)
LOG.info("Loading network driver '%s'", network_driver)
return importutils.import_module(network_driver)

659
nova/network/floating_ips.py

@ -1,659 +0,0 @@
# Copyright (c) 2011 X.commerce, a business unit of eBay Inc.
# Copyright 2010 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# 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 oslo_concurrency import processutils
from oslo_log import log as logging
import oslo_messaging as messaging
from oslo_utils import excutils
from oslo_utils import importutils
from oslo_utils import uuidutils
import six
import nova.conf
from nova import context
from nova.db import base
from nova import exception
from nova.network import rpcapi as network_rpcapi
from nova import objects
from nova import rpc
from nova import servicegroup
from nova import utils
LOG = logging.getLogger(__name__)
CONF = nova.conf.CONF
class FloatingIP(object):
"""Mixin class for adding floating IP functionality to a manager."""
servicegroup_api = None
def init_host_floating_ips(self):
"""Configures floating IPs owned by host."""
admin_context = context.get_admin_context()
try:
floating_ips = objects.FloatingIPList.get_by_host(admin_context,
self.host)
except exception.NotFound:
return
for floating_ip in floating_ips:
if floating_ip.fixed_ip_id:
try:
fixed_ip = floating_ip.fixed_ip
except exception.FixedIpNotFound:
LOG.debug('Fixed IP %s not found', floating_ip.fixed_ip_id)
continue
interface = CONF.public_interface or floating_ip.interface
try:
self.l3driver.add_floating_ip(floating_ip.address,
fixed_ip.address,
interface,
fixed_ip.network)
except processutils.ProcessExecutionError:
LOG.debug('Interface %s not found', interface)
raise exception.NoFloatingIpInterface(interface=interface)
def allocate_for_instance(self, context, **kwargs):
"""Handles allocating the floating IP resources for an instance.
calls super class allocate_for_instance() as well
rpc.called by network_api
"""
instance_uuid = kwargs.get('instance_id')
if not uuidutils.is_uuid_like(instance_uuid):
instance_uuid = kwargs.get('instance_uuid')
project_id = kwargs.get('project_id')
# call the next inherited class's allocate_for_instance()
# which is currently the NetworkManager version
# do this first so fixed ip is already allocated
nw_info = super(FloatingIP, self).allocate_for_instance(context,
**kwargs)
if CONF.auto_assign_floating_ip:
context = context.elevated()
# allocate a floating ip
floating_address = self.allocate_floating_ip(context, project_id,
True)
LOG.debug("floating IP allocation for instance "
"|%s|", floating_address,
instance_uuid=instance_uuid)
# get the first fixed address belonging to the instance
fixed_ips = nw_info.fixed_ips()
fixed_address = fixed_ips[0]['address']
# associate the floating ip to fixed_ip
self.associate_floating_ip(context,
floating_address,
fixed_address,
affect_auto_assigned=True)
# create a fresh set of network info that contains the floating ip
nw_info = self.get_instance_nw_info(context, **kwargs)
return nw_info
def deallocate_for_instance(self, context, **kwargs):
"""Handles deallocating floating IP resources for an instance.
calls super class deallocate_for_instance() as well.
rpc.called by network_api
"""
if 'instance' in kwargs:
instance_uuid = kwargs['instance'].uuid
else:
instance_uuid = kwargs['instance_id']
if not uuidutils.is_uuid_like(instance_uuid):
# NOTE(francois.charlier): in some cases the instance might be
# deleted before the IPs are released, so we need to get
# deleted instances too
instance = objects.Instance.get_by_id(
context.elevated(read_deleted='yes'), instance_uuid)
instance_uuid = instance.uuid
try:
fixed_ips = objects.FixedIPList.get_by_instance_uuid(
context, instance_uuid)
except exception.FixedIpNotFoundForInstance:
fixed_ips = []
# add to kwargs so we can pass to super to save a db lookup there
kwargs['fixed_ips'] = fixed_ips
for fixed_ip in fixed_ips:
fixed_id = fixed_ip.id
floating_ips = objects.FloatingIPList.get_by_fixed_ip_id(context,
fixed_id)
# disassociate floating ips related to fixed_ip
for floating_ip in floating_ips:
address = str(floating_ip.address)
try:
self.disassociate_floating_ip(context,
address,
affect_auto_assigned=True)
except exception.FloatingIpNotAssociated:
LOG.info("Floating IP %s is not associated. Ignore.",
address)
# deallocate if auto_assigned
if floating_ip.auto_assigned:
self.deallocate_floating_ip(context, address,
affect_auto_assigned=True)
# call the next inherited class's deallocate_for_instance()
# which is currently the NetworkManager version
# call this after so floating IPs are handled first
super(FloatingIP, self).deallocate_for_instance(context, **kwargs)
def _floating_ip_owned_by_project(self, context, floating_ip):
"""Raises if floating IP does not belong to project."""
if context.is_admin:
return
if floating_ip.project_id != context.project_id:
if floating_ip.project_id is None:
LOG.warning('Address |%(address)s| is not allocated',
{'address': floating_ip.address})
raise exception.Forbidden()
else:
LOG.warning('Address |%(address)s| is not allocated '
'to your project |%(project)s|',
{'address': floating_ip.address,
'project': context.project_id})
raise exception.Forbidden()
def _floating_ip_pool_exists(self, context, name):
"""Returns true if the specified floating IP pool exists. Otherwise,
returns false.
"""
pools = [pool.get('name') for pool in
self.get_floating_ip_pools(context)]
if name in pools:
return True
return False
def allocate_floating_ip(self, context, project_id, auto_assigned=False,
pool=None):
"""Gets a floating IP from the pool."""
# NOTE(tr3buchet): all network hosts in zone now use the same pool
pool = pool or CONF.default_floating_pool
use_quota = not auto_assigned
if not self._floating_ip_pool_exists(context, pool):
raise exception.FloatingIpPoolNotFound()
# Check the quota; can't put this in the API because we get
# called into from other places
try:
if use_quota:
objects.Quotas.check_deltas(context, {'floating_ips': 1},
project_id)
except exception.OverQuota:
LOG.warning("Quota exceeded for %s, tried to allocate "
"floating IP", context.project_id)
raise exception.FloatingIpLimitExceeded()
floating_ip = objects.FloatingIP.allocate_address(
context, project_id, pool, auto_assigned=auto_assigned)
# NOTE(melwitt): We recheck the quota after creating the object to
# prevent users from allocating more resources than their allowed quota
# in the event of a race. This is configurable because it can be
# expensive if strict quota limits are not required in a deployment.
if CONF.quota.recheck_quota and use_quota:
try:
objects.Quotas.check_deltas(context, {'floating_ips': 0},
project_id)
except exception.OverQuota:
objects.FloatingIP.deallocate(context, floating_ip.address)
LOG.warning("Quota exceeded for %s, tried to allocate "
"floating IP", context.project_id)
raise exception.FloatingIpLimitExceeded()
payload = dict(project_id=project_id, floating_ip=floating_ip)
self.notifier.info(context,
'network.floating_ip.allocate', payload)
return floating_ip
@messaging.expected_exceptions(exception.FloatingIpNotFoundForAddress)
def deallocate_floating_ip(self, context, address,
affect_auto_assigned=False):
"""Returns a floating IP to the pool."""
floating_ip = objects.FloatingIP.get_by_address(context, address)
# handle auto_assigned
if not affect_auto_assigned and floating_ip.auto_assigned:
return
# make sure project owns this floating ip (allocated)
self._floating_ip_owned_by_project(context, floating_ip)
# make sure floating ip is not associated
if floating_ip.fixed_ip_id:
floating_address = floating_ip.address
raise exception.FloatingIpAssociated(address=floating_address)
# clean up any associated DNS entries
self._delete_all_entries_for_ip(context,
floating_ip.address)
payload = dict(project_id=floating_ip.project_id,
floating_ip=str(floating_ip.address))
self.notifier.info(context, 'network.floating_ip.deallocate', payload)
objects.FloatingIP.deallocate(context, address)
@messaging.expected_exceptions(exception.FloatingIpNotFoundForAddress)
def associate_floating_ip(self, context, floating_address, fixed_address,
affect_auto_assigned=False):
"""Associates a floating IP with a fixed IP.
Makes sure everything makes sense then calls _associate_floating_ip,
rpc'ing to correct host if i'm not it.
Access to the floating_address is verified but access to the
fixed_address is not verified. This assumes that the calling
side has already verified that the fixed_address is legal by
checking access to the instance.
"""
floating_ip = objects.FloatingIP.get_by_address(context,
floating_address)
# handle auto_assigned
if not affect_auto_assigned and floating_ip.auto_assigned:
return
# make sure project owns this floating ip (allocated)
self._floating_ip_owned_by_project(context, floating_ip)
# disassociate any already associated
orig_instance_uuid = None
if floating_ip.fixed_ip_id:
# find previously associated instance
fixed_ip = floating_ip.fixed_ip
if str(fixed_ip.address) == fixed_address:
# NOTE(vish): already associated to this address
return
orig_instance_uuid = fixed_ip.instance_uuid
self.disassociate_floating_ip(context, floating_address)
fixed_ip = objects.FixedIP.get_by_address(context, fixed_address)
# send to correct host, unless i'm the correct host
network = objects.Network.get_by_id(context.elevated(),
fixed_ip.network_id)
if network.multi_host:
instance = objects.Instance.get_by_uuid(
context, fixed_ip.instance_uuid)
host = instance.host
else:
host = network.host
interface = floating_ip.interface
if host == self.host:
# i'm the correct host
self._associate_floating_ip(context, floating_address,
fixed_address, interface,
fixed_ip.instance_uuid)
else:
# send to correct host
self.network_rpcapi._associate_floating_ip(context,
floating_address, fixed_address, interface, host,
fixed_ip.instance_uuid)
return orig_instance_uuid
def _associate_floating_ip(self, context, floating_address, fixed_address,
interface, instance_uuid):
"""Performs db and driver calls to associate floating IP & fixed IP."""
interface = CONF.public_interface or interface
@utils.synchronized(six.text_type(floating_address))
def do_associate():
# associate floating ip
floating = objects.FloatingIP.associate(context, floating_address,
fixed_address, self.host)
fixed = floating.fixed_ip
if not fixed:
# NOTE(vish): ip was already associated