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
This commit is contained in:
parent
828f3f2691
commit
f5f73b4c4e
@ -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
|
@ -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
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
1336
nova/conf/network.py
File diff suppressed because it is too large
Load Diff
@ -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'.
|
||||
|
@ -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)
|
||||
|
||||
|
||||
####################
|
||||
|
||||
|
||||
|
@ -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"
|
||||
|
@ -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'
|
||||
|
@ -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
|
@ -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]])
|
@ -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()
|
@ -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]])
|
@ -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()
|
||||
|
@ -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})
|
@ -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
|
||||
|
@ -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()
|
@ -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)
|
@ -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
|
||||
return
|
||||
try:
|
||||
# gogo driver time
|
||||
self.l3driver.add_floating_ip(floating_address, fixed_address,
|
||||
interface, fixed['network'])
|
||||
except processutils.ProcessExecutionError as e:
|
||||
with excutils.save_and_reraise_exception():
|
||||
try:
|
||||
objects.FloatingIP.disassociate(context,
|
||||
floating_address)
|
||||
except Exception:
|
||||
LOG.warning('Failed to disassociated floating '
|
||||
'address: %s', floating_address)
|
||||
pass
|
||||
if "Cannot find device" in six.text_type(e):
|
||||
try:
|
||||
LOG.error('Interface %s not found', interface)
|
||||
except Exception:
|
||||
pass
|
||||
raise exception.NoFloatingIpInterface(
|
||||
interface=interface)
|
||||
|
||||
payload = dict(project_id=context.project_id,
|
||||
instance_id=instance_uuid,
|
||||
floating_ip=floating_address)
|
||||
self.notifier.info(context,
|
||||
'network.floating_ip.associate', payload)
|
||||
do_associate()
|
||||
|
||||
@messaging.expected_exceptions(exception.FloatingIpNotFoundForAddress)
|
||||
def disassociate_floating_ip(self, context, address,
|
||||
affect_auto_assigned=False):
|
||||
"""Disassociates a floating IP from its fixed IP.
|
||||
|
||||
Makes sure everything makes sense then calls _disassociate_floating_ip,
|
||||
rpc'ing to correct host if i'm not it.
|
||||
"""
|
||||
floating_ip = objects.FloatingIP.get_by_address(context, address)
|
||||
|
||||
# handle auto assigned
|
||||
if not affect_auto_assigned and floating_ip.auto_assigned:
|
||||
raise exception.CannotDisassociateAutoAssignedFloatingIP()
|
||||
|
||||
# make sure project owns this floating ip (allocated)
|
||||
self._floating_ip_owned_by_project(context, floating_ip)
|
||||
|
||||
# make sure floating ip is associated
|
||||
if not floating_ip.fixed_ip_id:
|
||||
floating_address = floating_ip.address
|
||||
raise exception.FloatingIpNotAssociated(address=floating_address)
|
||||
|
||||
fixed_ip = objects.FixedIP.get_by_id(context, floating_ip.fixed_ip_id)
|
||||
|
||||
# send to correct host, unless i'm the correct host
|
||||
network = objects.Network.get_by_id(context.elevated(),
|
||||
fixed_ip.network_id)
|
||||
interface = floating_ip.interface
|
||||
if network.multi_host:
|
||||
instance = objects.Instance.get_by_uuid(
|
||||
context, fixed_ip.instance_uuid)
|
||||
service = objects.Service.get_by_host_and_binary(
|
||||
context.elevated(), instance.host, 'nova-network')
|
||||
if service and self.servicegroup_api.service_is_up(service):
|
||||
host = instance.host
|
||||
else:
|
||||
# NOTE(vish): if the service is down just deallocate the data
|
||||
# locally. Set the host to local so the call will
|
||||
# not go over rpc and set interface to None so the
|
||||
# teardown in the driver does not happen.
|
||||
host = self.host
|
||||
interface = None
|
||||
else:
|
||||
host = network.host
|
||||
|
||||
if host == self.host:
|
||||
# i'm the correct host
|
||||
self._disassociate_floating_ip(context, address, interface,
|
||||
fixed_ip.instance_uuid)
|
||||
else:
|
||||
# send to correct host
|
||||
self.network_rpcapi._disassociate_floating_ip(context, address,
|
||||
interface, host, fixed_ip.instance_uuid)
|
||||
|
||||
def _disassociate_floating_ip(self, context, address, interface,
|
||||
instance_uuid):
|
||||
"""Performs db and driver calls to disassociate floating IP."""
|
||||
interface = CONF.public_interface or interface
|
||||
|
||||
@utils.synchronized(six.text_type(address))
|
||||
def do_disassociate():
|
||||
# NOTE(vish): Note that we are disassociating in the db before we
|
||||
# actually remove the ip address on the host. We are
|
||||
# safe from races on this host due to the decorator,
|
||||
# but another host might grab the ip right away. We
|
||||
# don't worry about this case because the minuscule
|
||||
# window where the ip is on both hosts shouldn't cause
|
||||
# any problems.
|
||||
floating = objects.FloatingIP.disassociate(context, address)
|
||||
fixed = floating.fixed_ip
|
||||
if not fixed:
|
||||
# NOTE(vish): ip was already disassociated
|
||||
return
|
||||
if interface:
|
||||
# go go driver time
|
||||
self.l3driver.remove_floating_ip(address, fixed.address,
|
||||
interface, fixed.network)
|
||||
payload = dict(project_id=context.project_id,
|
||||
instance_id=instance_uuid,
|
||||
floating_ip=address)
|
||||
self.notifier.info(context,
|
||||
'network.floating_ip.disassociate', payload)
|
||||
do_disassociate()
|
||||
|
||||
@messaging.expected_exceptions(exception.FloatingIpNotFound)
|
||||
def get_floating_ip(self, context, id):
|
||||
"""Returns a floating IP as a dict."""
|
||||
# NOTE(vish): This is no longer used but can't be removed until
|
||||
# we major version the network_rpcapi.
|
||||
return dict(objects.FloatingIP.get_by_id(context, id))
|
||||
|
||||
def get_floating_pools(self, context):
|
||||
"""Returns list of floating pools."""
|
||||
# NOTE(maurosr) This method should be removed in future, replaced by
|
||||
# get_floating_ip_pools. See bug #1091668
|
||||
return self.get_floating_ip_pools(context)
|
||||
|
||||
def get_floating_ip_pools(self, context):
|
||||
"""Returns list of floating ip pools."""
|
||||
# NOTE(vish): This is no longer used but can't be removed until
|
||||
# we major version the network_rpcapi.
|
||||
pools = objects.FloatingIP.get_pool_names(context)
|
||||
return [dict(name=name) for name in pools]
|
||||
|
||||
def get_floating_ip_by_address(self, context, address):
|
||||
"""Returns a floating IP as a dict."""
|
||||
# NOTE(vish): This is no longer used but can't be removed until
|
||||
# we major version the network_rpcapi.
|
||||
return objects.FloatingIP.get_by_address(context, address)
|
||||
|
||||
def get_floating_ips_by_project(self, context):
|
||||
"""Returns the floating IPs allocated to a project."""
|
||||
# NOTE(vish): This is no longer used but can't be removed until
|
||||
# we major version the network_rpcapi.
|
||||
return objects.FloatingIPList.get_by_project(context,
|
||||
context.project_id)
|
||||
|
||||
def get_floating_ips_by_fixed_address(self, context, fixed_address):
|
||||
"""Returns the floating IPs associated with a fixed_address."""
|
||||
# NOTE(vish): This is no longer used but can't be removed until
|
||||
# we major version the network_rpcapi.
|
||||
floating_ips = objects.FloatingIPList.get_by_fixed_address(
|
||||
context, fixed_address)
|
||||
return [str(floating_ip.address) for floating_ip in floating_ips]
|
||||
|
||||
def _is_stale_floating_ip_address(self, context, floating_ip):
|
||||
try:
|
||||
self._floating_ip_owned_by_project(context, floating_ip)
|
||||
except exception.Forbidden:
|
||||
return True
|
||||
return False if floating_ip.get('fixed_ip_id') else True
|
||||
|
||||
def migrate_instance_start(self, context, instance_uuid,
|
||||
floating_addresses,
|
||||
rxtx_factor=None, project_id=None,
|
||||
source=None, dest=None):
|
||||
# We only care if floating_addresses are provided and we're
|
||||
# switching hosts
|
||||
if not floating_addresses or (source and source == dest):
|
||||
return
|
||||
|
||||
LOG.info("Starting migration network for instance %s", instance_uuid)
|
||||
for address in floating_addresses:
|
||||
floating_ip = objects.FloatingIP.get_by_address(context, address)
|
||||
|
||||
if self._is_stale_floating_ip_address(context, floating_ip):
|
||||
LOG.warning("Floating IP address |%(address)s| no longer "
|
||||
"belongs to instance %(instance_uuid)s. "
|
||||
"Will not migrate it ",
|
||||
{'address': address,
|
||||
'instance_uuid': instance_uuid})
|
||||
continue
|
||||
|
||||
interface = CONF.public_interface or floating_ip.interface
|
||||
fixed_ip = floating_ip.fixed_ip
|
||||
self.l3driver.remove_floating_ip(floating_ip.address,
|
||||
fixed_ip.address,
|
||||
interface,
|
||||
fixed_ip.network)
|
||||
|
||||
# NOTE(wenjianhn): Make this address will not be bound to public
|
||||
# interface when restarts nova-network on dest compute node
|
||||
floating_ip.host = None
|
||||
floating_ip.save()
|
||||
|
||||
def migrate_instance_finish(self, context, instance_uuid,
|
||||
floating_addresses, host=None,
|
||||
rxtx_factor=None, project_id=None,
|
||||
source=None, dest=None):
|
||||
# We only care if floating_addresses are provided and we're
|
||||
# switching hosts
|
||||
if host and not dest:
|
||||
dest = host
|
||||
if not floating_addresses or (source and source == dest):
|
||||
return
|
||||
|
||||
LOG.info("Finishing migration network for instance %s", instance_uuid)
|
||||
|
||||
for address in floating_addresses:
|
||||
floating_ip = objects.FloatingIP.get_by_address(context, address)
|
||||
|
||||
if self._is_stale_floating_ip_address(context, floating_ip):
|
||||
LOG.warning("Floating IP address |%(address)s| no longer "
|
||||
"belongs to instance %(instance_uuid)s. "
|
||||
"Will not setup it.",
|
||||
{'address': address,
|
||||
'instance_uuid': instance_uuid})
|
||||
continue
|
||||
|
||||
floating_ip.host = dest
|
||||
floating_ip.save()
|
||||
|
||||
interface = CONF.public_interface or floating_ip.interface
|
||||
fixed_ip = floating_ip.fixed_ip
|
||||
self.l3driver.add_floating_ip(floating_ip.address,
|
||||
fixed_ip.address,
|
||||
interface,
|
||||
fixed_ip.network)
|
||||
|
||||
def _prepare_domain_entry(self, context, domainref):
|
||||
scope = domainref.scope
|
||||
if scope == 'private':
|
||||
this_domain = {'domain': domainref.domain,
|
||||
'scope': scope,
|
||||
'availability_zone': domainref.availability_zone}
|
||||
else:
|
||||
this_domain = {'domain': domainref.domain,
|
||||
'scope': scope,
|
||||
'project': domainref.project_id}
|
||||
return this_domain
|
||||
|
||||
def get_dns_domains(self, context):
|
||||
domains = []
|
||||
|
||||
domain_list = objects.DNSDomainList.get_all(context)
|
||||
floating_driver_domain_list = self.floating_dns_manager.get_domains()
|
||||
instance_driver_domain_list = self.instance_dns_manager.get_domains()
|
||||
|
||||
for dns_domain in domain_list:
|
||||
if (dns_domain.domain in floating_driver_domain_list or
|
||||
dns_domain.domain in instance_driver_domain_list):
|
||||
domain_entry = self._prepare_domain_entry(context, dns_domain)
|
||||
if domain_entry:
|
||||
domains.append(domain_entry)
|
||||
else:
|
||||
LOG.warning('Database inconsistency: DNS domain |%s| is '
|
||||
'registered in the Nova db but not visible to '
|
||||
'either the floating or instance DNS driver. '
|
||||
'It will be ignored.', dns_domain.domain)
|
||||
|
||||
return domains
|
||||
|
||||
def add_dns_entry(self, context, address, name, dns_type, domain):
|
||||
self.floating_dns_manager.create_entry(name, address,
|
||||
dns_type, domain)
|
||||
|
||||
def modify_dns_entry(self, context, address, name, domain):
|
||||
self.floating_dns_manager.modify_address(name, address,
|
||||
domain)
|
||||
|
||||
def delete_dns_entry(self, context, name, domain):
|
||||
self.floating_dns_manager.delete_entry(name, domain)
|
||||
|
||||
def _delete_all_entries_for_ip(self, context, address):
|
||||
domain_list = self.get_dns_domains(context)
|
||||
for domain in domain_list:
|
||||
names = self.get_dns_entries_by_address(context,
|
||||
address,
|
||||
domain['domain'])
|
||||
for name in names:
|
||||
self.delete_dns_entry(context, name, domain['domain'])
|
||||
|
||||
def get_dns_entries_by_address(self, context, address, domain):
|
||||
return self.floating_dns_manager.get_entries_by_address(address,
|
||||
domain)
|
||||
|
||||
def get_dns_entries_by_name(self, context, name, domain):
|
||||
return self.floating_dns_manager.get_entries_by_name(name,
|
||||
domain)
|
||||
|
||||
def create_private_dns_domain(self, context, domain, av_zone):
|
||||
objects.DNSDomain.register_for_zone(context, domain, av_zone)
|
||||
try:
|
||||
self.instance_dns_manager.create_domain(domain)
|
||||
except exception.FloatingIpDNSExists:
|
||||
LOG.warning('Domain |%(domain)s| already exists, '
|
||||
'changing zone to |%(av_zone)s|.',
|
||||
{'domain': domain, 'av_zone': av_zone})
|
||||
|
||||
def create_public_dns_domain(self, context, domain, project):
|
||||
objects.DNSDomain.register_for_project(context, domain, project)
|
||||
try:
|
||||
self.floating_dns_manager.create_domain(domain)
|
||||
except exception.FloatingIpDNSExists:
|
||||
LOG.warning('Domain |%(domain)s| already exists, '
|
||||
'changing project to |%(project)s|.',
|
||||
{'domain': domain, 'project': project})
|
||||
|
||||
def delete_dns_domain(self, context, domain):
|
||||
objects.DNSDomain.delete_by_domain(context, domain)
|
||||
self.floating_dns_manager.delete_domain(domain)
|
||||
|
||||
|
||||
class LocalManager(base.Base, FloatingIP):
|
||||
def __init__(self):
|
||||
super(LocalManager, self).__init__()
|
||||
# NOTE(vish): setting the host to none ensures that the actual
|
||||
# l3driver commands for l3 are done via rpc.
|
||||
self.host = None
|
||||
self.servicegroup_api = servicegroup.API()
|
||||
self.network_rpcapi = network_rpcapi.NetworkAPI()
|
||||
self.floating_dns_manager = importutils.import_object(
|
||||
CONF.floating_ip_dns_manager)
|
||||
self.instance_dns_manager = importutils.import_object(
|
||||
CONF.instance_dns_manager)
|
||||
self.notifier = rpc.get_notifier('network', CONF.host)
|
@ -1,179 +0,0 @@
|
||||
# Copyright 2012 Nicira Networks, Inc
|
||||
# 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_log import log as logging
|
||||
|
||||
from nova.network import linux_net
|
||||
import nova.privsep.linux_net
|
||||
from nova import utils
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class L3Driver(object):
|
||||
"""Abstract class that defines a generic L3 API."""
|
||||
|
||||
def __init__(self, l3_lib=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def initialize(self, **kwargs):
|
||||
"""Set up basic L3 networking functionality."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def initialize_network(self, cidr, is_external):
|
||||
"""Enable rules for a specific network."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def initialize_gateway(self, network_ref):
|
||||
"""Set up a gateway on this network."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def remove_gateway(self, network_ref):
|
||||
"""Remove an existing gateway on this network."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def is_initialized(self):
|
||||
""":returns: True/False (whether the driver is initialized)."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
|
||||
network=None):
|
||||
"""Add a floating IP bound to the fixed IP with an optional
|
||||
l3_interface_id. Some drivers won't care about the
|
||||
l3_interface_id so just pass None in that case. Network
|
||||
is also an optional parameter.
|
||||
"""
|
||||
raise NotImplementedError()
|
||||
|
||||
def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
|
||||
network=None):
|
||||
raise NotImplementedError()
|
||||
|
||||
def add_vpn(self, public_ip, port, private_ip):
|
||||
raise NotImplementedError()
|
||||
|
||||
def remove_vpn(self, public_ip, port, private_ip):
|
||||
raise NotImplementedError()
|
||||
|
||||
def clean_conntrack(self, fixed_ip):
|
||||
raise NotImplementedError()
|
||||
|
||||
def teardown(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
|
||||
class LinuxNetL3(L3Driver):
|
||||
"""L3 driver that uses linux_net as the backend."""
|
||||
def __init__(self):
|
||||
self.initialized = False
|
||||
|
||||
def initialize(self, **kwargs):
|
||||
if self.initialized:
|
||||
return
|
||||
LOG.debug("Initializing linux_net L3 driver")
|
||||
fixed_range = kwargs.get('fixed_range', False)
|
||||
networks = kwargs.get('networks', None)
|
||||
if not fixed_range and networks is not None:
|
||||
for network in networks:
|
||||
if network['enable_dhcp']:
|
||||
is_ext = (network['dhcp_server'] is not None and
|
||||
network['dhcp_server'] != network['gateway'])
|
||||
self.initialize_network(network['cidr'], is_ext)
|
||||
linux_net.ensure_metadata_ip()
|
||||
linux_net.metadata_forward()
|
||||
self.initialized = True
|
||||
|
||||
def is_initialized(self):
|
||||
return self.initialized
|
||||
|
||||
def initialize_network(self, cidr, is_external):
|
||||
linux_net.init_host(cidr, is_external)
|
||||
|
||||
def initialize_gateway(self, network_ref):
|
||||
mac_address = utils.generate_mac_address()
|
||||
dev = linux_net.plug(network_ref, mac_address,
|
||||
gateway=(network_ref['gateway'] is not None))
|
||||
linux_net.initialize_gateway_device(dev, network_ref)
|
||||
|
||||
def remove_gateway(self, network_ref):
|
||||
linux_net.unplug(network_ref)
|
||||
|
||||
def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
|
||||
network=None):
|
||||
linux_net.ensure_floating_forward(floating_ip, fixed_ip,
|
||||
l3_interface_id, network)
|
||||
linux_net.bind_floating_ip(floating_ip, l3_interface_id)
|
||||
|
||||
def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
|
||||
network=None):
|
||||
nova.privsep.linux_net.unbind_ip(l3_interface_id, floating_ip)
|
||||
linux_net.remove_floating_forward(floating_ip, fixed_ip,
|
||||
l3_interface_id, network)
|
||||
nova.privsep.linux_net.clean_conntrack(fixed_ip)
|
||||
|
||||
def add_vpn(self, public_ip, port, private_ip):
|
||||
linux_net.ensure_vpn_forward(public_ip, port, private_ip)
|
||||
|
||||
def remove_vpn(self, public_ip, port, private_ip):
|
||||
# Linux net currently doesn't implement any way of removing
|
||||
# the VPN forwarding rules
|
||||
pass
|
||||
|
||||
def teardown(self):
|
||||
pass
|
||||
|
||||
|
||||
class NullL3(L3Driver):
|
||||
"""The L3 driver that doesn't do anything. This class can be used when
|
||||
nova-network should not manipulate L3 forwarding at all (e.g., in a Flat
|
||||
or FlatDHCP scenario).
|
||||
"""
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def initialize(self, **kwargs):
|
||||
pass
|
||||
|
||||
def is_initialized(self):
|
||||
return True
|
||||
|
||||
def initialize_network(self, cidr, is_external):
|
||||
pass
|
||||
|
||||
def initialize_gateway(self, network_ref):
|
||||
pass
|
||||
|
||||
def remove_gateway(self, network_ref):
|
||||
pass
|
||||
|
||||
def add_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
|
||||
network=None):
|
||||
pass
|
||||
|
||||
def remove_floating_ip(self, floating_ip, fixed_ip, l3_interface_id,
|
||||
network=None):
|
||||
pass
|
||||
|
||||
def add_vpn(self, public_ip, port, private_ip):
|
||||
pass
|
||||
|
||||
def remove_vpn(self, public_ip, port, private_ip):
|
||||
pass
|
||||
|
||||
def clean_conntrack(self, fixed_ip):
|
||||
pass
|
||||
|
||||
def teardown(self):
|
||||
pass
|
@ -1,337 +0,0 @@
|
||||
# Copyright 2012 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.
|
||||
|
||||
try:
|
||||
import ldap
|
||||
except ImportError:
|
||||
# This module needs to be importable despite ldap not being a requirement
|
||||
ldap = None
|
||||
|
||||
import time
|
||||
|
||||
from oslo_log import log as logging
|
||||
|
||||
import nova.conf
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.network import dns_driver
|
||||
from nova import utils
|
||||
|
||||
CONF = nova.conf.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
# Importing ldap.modlist breaks the tests for some reason,
|
||||
# so this is an abbreviated version of a function from
|
||||
# there.
|
||||
def create_modlist(newattrs):
|
||||
modlist = []
|
||||
for attrtype in newattrs.keys():
|
||||
utf8_vals = []
|
||||
for val in newattrs[attrtype]:
|
||||
utf8_vals.append(utils.utf8(val))
|
||||
newattrs[attrtype] = utf8_vals
|
||||
modlist.append((attrtype, newattrs[attrtype]))
|
||||
return modlist
|
||||
|
||||
|
||||
class DNSEntry(object):
|
||||
|
||||
def __init__(self, ldap_object):
|
||||
"""ldap_object is an instance of ldap.LDAPObject.
|
||||
|
||||
It should already be initialized and bound before
|
||||
getting passed in here.
|
||||
"""
|
||||
self.lobj = ldap_object
|
||||
self.ldap_tuple = None
|
||||
self.qualified_domain = None
|
||||
|
||||
@classmethod
|
||||
def _get_tuple_for_domain(cls, lobj, domain):
|
||||
entry = lobj.search_s(CONF.ldap_dns_base_dn, ldap.SCOPE_SUBTREE,
|
||||
'(associatedDomain=%s)' % utils.utf8(domain))
|
||||
if not entry:
|
||||
return None
|
||||
if len(entry) > 1:
|
||||
LOG.warning("Found multiple matches for domain "
|
||||
"%(domain)s.\n%(entry)s",
|
||||
domain, entry)
|
||||
return entry[0]
|
||||
|
||||
@classmethod
|
||||
def _get_all_domains(cls, lobj):
|
||||
entries = lobj.search_s(CONF.ldap_dns_base_dn,
|
||||
ldap.SCOPE_SUBTREE, '(sOARecord=*)')
|
||||
domains = []
|
||||
for entry in entries:
|
||||
domain = entry[1].get('associatedDomain')
|
||||
if domain:
|
||||
domains.append(domain[0])
|
||||
return domains
|
||||
|
||||
def _set_tuple(self, tuple):
|
||||
self.ldap_tuple = tuple
|
||||
|
||||
def _qualify(self, name):
|
||||
return '%s.%s' % (name, self.qualified_domain)
|
||||
|
||||
def _dequalify(self, name):
|
||||
z = ".%s" % self.qualified_domain
|
||||
if name.endswith(z):
|
||||
dequalified = name[0:name.rfind(z)]
|
||||
else:
|
||||
LOG.warning("Unable to dequalify. %(name)s is not in "
|
||||
"%(domain)s.\n",
|
||||
{'name': name,
|
||||
'domain': self.qualified_domain})
|
||||
dequalified = None
|
||||
|
||||
return dequalified
|
||||
|
||||
def _dn(self):
|
||||
return self.ldap_tuple[0]
|
||||
dn = property(_dn)
|
||||
|
||||
def _rdn(self):
|
||||
return self.dn.partition(',')[0]
|
||||
rdn = property(_rdn)
|
||||
|
||||
|
||||
class DomainEntry(DNSEntry):
|
||||
|
||||
@classmethod
|
||||
def _soa(cls):
|
||||
date = time.strftime('%Y%m%d%H%M%S')
|
||||
soa = '%s %s %s %d %d %d %d' % (
|
||||
CONF.ldap_dns_servers[0],
|
||||
CONF.ldap_dns_soa_hostmaster,
|
||||
date,
|
||||
CONF.ldap_dns_soa_refresh,
|
||||
CONF.ldap_dns_soa_retry,
|
||||
CONF.ldap_dns_soa_expiry,
|
||||
CONF.ldap_dns_soa_minimum)
|
||||
return utils.utf8(soa)
|
||||
|
||||
@classmethod
|
||||
def create_domain(cls, lobj, domain):
|
||||
"""Create a new domain entry, and return an object that wraps it."""
|
||||
entry = cls._get_tuple_for_domain(lobj, domain)
|
||||
if entry:
|
||||
raise exception.FloatingIpDNSExists(name=domain, domain='')
|
||||
|
||||
newdn = 'dc=%s,%s' % (domain, CONF.ldap_dns_base_dn)
|
||||
attrs = {'objectClass': ['domainrelatedobject', 'dnsdomain',
|
||||
'domain', 'dcobject', 'top'],
|
||||
'sOARecord': [cls._soa()],
|
||||
'associatedDomain': [domain],
|
||||
'dc': [domain]}
|
||||
lobj.add_s(newdn, create_modlist(attrs))
|
||||
return DomainEntry(lobj, domain)
|
||||
|
||||
def __init__(self, ldap_object, domain):
|
||||
super(DomainEntry, self).__init__(ldap_object)
|
||||
entry = self._get_tuple_for_domain(self.lobj, domain)
|
||||
if not entry:
|
||||
raise exception.NotFound()
|
||||
self._set_tuple(entry)
|
||||
assert(entry[1]['associatedDomain'][0] == domain)
|
||||
self.qualified_domain = domain
|
||||
|
||||
def delete(self):
|
||||
"""Delete the domain that this entry refers to."""
|
||||
entries = self.lobj.search_s(self.dn,
|
||||
ldap.SCOPE_SUBTREE,
|
||||
'(aRecord=*)')
|
||||
for entry in entries:
|
||||
self.lobj.delete_s(entry[0])
|
||||
|
||||
self.lobj.delete_s(self.dn)
|
||||
|
||||
def update_soa(self):
|
||||
mlist = [(ldap.MOD_REPLACE, 'sOARecord', self._soa())]
|
||||
self.lobj.modify_s(self.dn, mlist)
|
||||
|
||||
def subentry_with_name(self, name):
|
||||
entry = self.lobj.search_s(self.dn, ldap.SCOPE_SUBTREE,
|
||||
'(associatedDomain=%s.%s)' %
|
||||
(utils.utf8(name),
|
||||
utils.utf8(self.qualified_domain)))
|
||||
if entry:
|
||||
return HostEntry(self, entry[0])
|
||||
else:
|
||||
return None
|
||||
|
||||
def subentries_with_ip(self, ip):
|
||||
entries = self.lobj.search_s(self.dn, ldap.SCOPE_SUBTREE,
|
||||
'(aRecord=%s)' % utils.utf8(ip))
|
||||
objs = []
|
||||
for entry in entries:
|
||||
if 'associatedDomain' in entry[1]:
|
||||
objs.append(HostEntry(self, entry))
|
||||
|
||||
return objs
|
||||
|
||||
def add_entry(self, name, address):
|
||||
if self.subentry_with_name(name):
|
||||
raise exception.FloatingIpDNSExists(name=name,
|
||||
domain=self.qualified_domain)
|
||||
|
||||
entries = self.subentries_with_ip(address)
|
||||
if entries:
|
||||
# We already have an ldap entry for this IP, so we just
|
||||
# need to add the new name.
|
||||
existingdn = entries[0].dn
|
||||
self.lobj.modify_s(existingdn, [(ldap.MOD_ADD,
|
||||
'associatedDomain',
|
||||
utils.utf8(self._qualify(name)))])
|
||||
|
||||
return self.subentry_with_name(name)
|
||||
else:
|
||||
# We need to create an entirely new entry.
|
||||
newdn = 'dc=%s,%s' % (name, self.dn)
|
||||
attrs = {'objectClass': ['domainrelatedobject', 'dnsdomain',
|
||||
'domain', 'dcobject', 'top'],
|
||||
'aRecord': [address],
|
||||
'associatedDomain': [self._qualify(name)],
|
||||
'dc': [name]}
|
||||
self.lobj.add_s(newdn, create_modlist(attrs))
|
||||
return self.subentry_with_name(name)
|
||||
|
||||
def remove_entry(self, name):
|
||||
entry = self.subentry_with_name(name)
|
||||
if not entry:
|
||||
raise exception.NotFound()
|
||||
entry.remove_name(name)
|
||||
self.update_soa()
|
||||
|
||||
|
||||
class HostEntry(DNSEntry):
|
||||
|
||||
def __init__(self, parent, tuple):
|
||||
super(HostEntry, self).__init__(parent.lobj)
|
||||
self.parent_entry = parent
|
||||
self._set_tuple(tuple)
|
||||
self.qualified_domain = parent.qualified_domain
|
||||
|
||||
def remove_name(self, name):
|
||||
names = self.ldap_tuple[1]['associatedDomain']
|
||||
if not names:
|
||||
raise exception.NotFound()
|
||||
if len(names) > 1:
|
||||
# We just have to remove the requested domain.
|
||||
self.lobj.modify_s(self.dn, [(ldap.MOD_DELETE, 'associatedDomain',
|
||||
self._qualify(utils.utf8(name)))])
|
||||
if (self.rdn[1] == name):
|
||||
# We just removed the rdn, so we need to move this entry.
|
||||
names.remove(self._qualify(name))
|
||||
newrdn = 'dc=%s' % self._dequalify(names[0])
|
||||
self.lobj.modrdn_s(self.dn, [newrdn])
|
||||
else:
|
||||
# We should delete the entire record.
|
||||
self.lobj.delete_s(self.dn)
|
||||
|
||||
def modify_address(self, name, address):
|
||||
names = self.ldap_tuple[1]['associatedDomain']
|
||||
if not names:
|
||||
raise exception.NotFound()
|
||||
if len(names) == 1:
|
||||
self.lobj.modify_s(self.dn, [(ldap.MOD_REPLACE, 'aRecord',
|
||||
[utils.utf8(address)])])
|
||||
else:
|
||||
self.remove_name(name)
|
||||
self.parent.add_entry(name, address)
|
||||
|
||||
def _names(self):
|
||||
names = []
|
||||
for domain in self.ldap_tuple[1]['associatedDomain']:
|
||||
names.append(self._dequalify(domain))
|
||||
return names
|
||||
names = property(_names)
|
||||
|
||||
def _ip(self):
|
||||
ip = self.ldap_tuple[1]['aRecord'][0]
|
||||
return ip
|
||||
ip = property(_ip)
|
||||
|
||||
def _parent(self):
|
||||
return self.parent_entry
|
||||
parent = property(_parent)
|
||||
|
||||
|
||||
class LdapDNS(dns_driver.DNSDriver):
|
||||
"""Driver for PowerDNS using ldap as a back end.
|
||||
|
||||
This driver assumes ldap-method=strict, with all domains
|
||||
in the top-level, aRecords only.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
if not ldap:
|
||||
raise ImportError(_('ldap not installed'))
|
||||
|
||||
self.lobj = ldap.initialize(CONF.ldap_dns_url)
|
||||
self.lobj.simple_bind_s(CONF.ldap_dns_user,
|
||||
CONF.ldap_dns_password)
|
||||
|
||||
def get_domains(self):
|
||||
return DomainEntry._get_all_domains(self.lobj)
|
||||
|
||||
def create_entry(self, name, address, type, domain):
|
||||
if type.lower() != 'a':
|
||||
raise exception.InvalidInput(_("This driver only supports "
|
||||
"type 'a' entries."))
|
||||
|
||||
dEntry = DomainEntry(self.lobj, domain)
|
||||
dEntry.add_entry(name, address)
|
||||
|
||||
def delete_entry(self, name, domain):
|
||||
dEntry = DomainEntry(self.lobj, domain)
|
||||
dEntry.remove_entry(name)
|
||||
|
||||
def get_entries_by_address(self, address, domain):
|
||||
try:
|
||||
dEntry = DomainEntry(self.lobj, domain)
|
||||
except exception.NotFound:
|
||||
return []
|
||||
entries = dEntry.subentries_with_ip(address)
|
||||
names = []
|
||||
for entry in entries:
|
||||
names.extend(entry.names)
|
||||
return names
|
||||
|
||||
def get_entries_by_name(self, name, domain):
|
||||
try:
|
||||
dEntry = DomainEntry(self.lobj, domain)
|
||||
except exception.NotFound:
|
||||
return []
|
||||
nEntry = dEntry.subentry_with_name(name)
|
||||
if nEntry:
|
||||
return [nEntry.ip]
|
||||
|
||||
def modify_address(self, name, address, domain):
|
||||
dEntry = DomainEntry(self.lobj, domain)
|
||||
nEntry = dEntry.subentry_with_name(name)
|
||||
nEntry.modify_address(name, address)
|
||||
|
||||
def create_domain(self, domain):
|
||||
DomainEntry.create_domain(self.lobj, domain)
|
||||
|
||||
def delete_domain(self, domain):
|
||||
dEntry = DomainEntry(self.lobj, domain)
|
||||
dEntry.delete()
|
||||
|
||||
def delete_dns_file(self):
|
||||
LOG.warning("This shouldn't be getting called except during testing.")
|
||||
pass
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,206 +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.
|
||||
|
||||
import os
|
||||
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
import six
|
||||
|
||||
from nova import exception
|
||||
from nova.i18n import _
|
||||
from nova.network import dns_driver
|
||||
|
||||
|
||||
CONF = cfg.CONF
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class MiniDNS(dns_driver.DNSDriver):
|
||||
"""Trivial DNS driver. This will read/write to either a local,
|
||||
flat file or an in memory StringIO and have no effect on your actual
|
||||
DNS system. This class is strictly for testing purposes, and should
|
||||
keep you out of dependency hell.
|
||||
|
||||
A file is used when CONF.log_dir is set. This is relevant for when
|
||||
two different DNS driver instances share the same data file.
|
||||
|
||||
Note that there is almost certainly a race condition here that
|
||||
will manifest anytime instances are rapidly created and deleted.
|
||||
A proper implementation will need some manner of locking.
|
||||
"""
|
||||
|
||||
def __init__(self):
|
||||
filename = None
|
||||
if CONF.log_dir:
|
||||
filename = os.path.join(CONF.log_dir, "dnstest.txt")
|
||||
self.file = open(filename, 'w+')
|
||||
else:
|
||||
self.file = six.StringIO()
|
||||
if not filename or not os.path.exists(filename):
|
||||
self.file.write("# minidns\n\n\n")
|
||||
self.file.flush()
|
||||
|
||||
def get_domains(self):
|
||||
entries = []
|
||||
self.file.seek(0)
|
||||
for line in self.file:
|
||||
entry = self.parse_line(line)
|
||||
if entry and entry['address'] == 'domain':
|
||||
entries.append(entry['name'])
|
||||
return entries
|
||||
|
||||
def qualify(self, name, domain):
|
||||
if domain:
|
||||
qualified = "%s.%s" % (name, domain)
|
||||
else:
|
||||
qualified = name
|
||||
|
||||
return qualified.lower()
|
||||
|
||||
def create_entry(self, name, address, type, domain):
|
||||
if name is None:
|
||||
raise exception.InvalidInput(_("Invalid name"))
|
||||
|
||||
if type.lower() != 'a':
|
||||
raise exception.InvalidInput(_("This driver only supports "
|
||||
"type 'a'"))
|
||||
|
||||
if self.get_entries_by_name(name, domain):
|
||||
raise exception.FloatingIpDNSExists(name=name, domain=domain)
|
||||
|
||||
self.file.seek(0, os.SEEK_END)
|
||||
self.file.write("%s %s %s\n" %
|
||||
(address, self.qualify(name, domain), type))
|
||||
self.file.flush()
|
||||
|
||||
def parse_line(self, line):
|
||||
vals = line.split()
|
||||
if len(vals) < 3:
|
||||
return None
|
||||
else:
|
||||
entry = {}
|
||||
entry['address'] = vals[0].lower()
|
||||
entry['name'] = vals[1].lower()
|
||||
entry['type'] = vals[2].lower()
|
||||
if entry['address'] == 'domain':
|
||||
entry['domain'] = entry['name']
|
||||
else:
|
||||
entry['domain'] = entry['name'].partition('.')[2]
|
||||
return entry
|
||||
|
||||
def delete_entry(self, name, domain):
|
||||
if name is None:
|
||||
raise exception.InvalidInput(_("Invalid name"))
|
||||
|
||||
deleted = False
|
||||
keeps = []
|
||||
self.file.seek(0)
|
||||
for line in self.file:
|
||||
entry = self.parse_line(line)
|
||||
if (not entry or
|
||||
entry['name'] != self.qualify(name, domain)):
|
||||
keeps.append(line)
|
||||
else:
|
||||
deleted = True
|
||||
self.file.truncate(0)
|
||||
self.file.seek(0)
|
||||
self.file.write(''.join(keeps))
|
||||
self.file.flush()
|
||||
if not deleted:
|
||||
LOG.warning('Cannot delete entry |%s|', self.qualify(name, domain))
|
||||
raise exception.NotFound
|
||||
|
||||
def modify_address(self, name, address, domain):
|
||||
|
||||
if not self.get_entries_by_name(name, domain):
|
||||
raise exception.NotFound
|
||||
|
||||
lines = []
|
||||
self.file.seek(0)
|
||||
for line in self.file:
|
||||
entry = self.parse_line(line)
|
||||
if (entry and
|
||||
entry['name'] == self.qualify(name, domain)):
|
||||
lines.append("%s %s %s\n" %
|
||||
(address, self.qualify(name, domain), entry['type']))
|
||||
else:
|
||||
lines.append(line)
|
||||
self.file.truncate(0)
|
||||
self.file.seek(0)
|
||||
self.file.write(''.join(lines))
|
||||
self.file.flush()
|
||||
|
||||
def get_entries_by_address(self, address, domain):
|
||||
entries = []
|
||||
self.file.seek(0)
|
||||
for line in self.file:
|
||||
entry = self.parse_line(line)
|
||||
if entry and entry['address'] == address.lower():
|
||||
if entry['name'].endswith(domain.lower()):
|
||||
name = entry['name'].split(".")[0]
|
||||
if name not in entries:
|
||||
entries.append(name)
|
||||
|
||||
return entries
|
||||
|
||||
def get_entries_by_name(self, name, domain):
|
||||
entries = []
|
||||
self.file.seek(0)
|
||||
for line in self.file:
|
||||
entry = self.parse_line(line)
|
||||
if (entry and
|
||||
entry['name'] == self.qualify(name, domain)):
|
||||
entries.append(entry['address'])
|
||||
return entries
|
||||
|
||||
def delete_dns_file(self):
|
||||
self.file.close()
|
||||
try:
|
||||
if os.path.exists(self.file.name):
|
||||
try:
|
||||
os.remove(self.file.name)
|
||||
except OSError:
|
||||
pass
|
||||
except AttributeError:
|
||||
# This was a BytesIO, which has no name.
|
||||
pass
|
||||
|
||||
def create_domain(self, fqdomain):
|
||||
if self.get_entries_by_name(fqdomain, ''):
|
||||
raise exception.FloatingIpDNSExists(name=fqdomain, domain='')
|
||||
|
||||
self.file.seek(0, os.SEEK_END)
|
||||
self.file.write("%s %s %s\n" % ('domain', fqdomain, 'domain'))
|
||||
self.file.flush()
|
||||
|
||||
def delete_domain(self, fqdomain):
|
||||
deleted = False
|
||||
keeps = []
|
||||
self.file.seek(0)
|
||||
for line in self.file:
|
||||
entry = self.parse_line(line)
|
||||
if (not entry or
|
||||
entry['domain'] != fqdomain.lower()):
|
||||
keeps.append(line)
|
||||
else:
|
||||
LOG.info("deleted %s", entry)
|
||||
deleted = True
|
||||
self.file.truncate(0)
|
||||
self.file.seek(0)
|
||||
self.file.write(''.join(keeps))
|
||||
self.file.flush()
|
||||
if not deleted:
|
||||
LOG.warning('Cannot delete domain |%s|', fqdomain)
|
||||
raise exception.NotFound
|
@ -2496,14 +2496,6 @@ class API(base_api.NetworkAPI):
|
||||
uuid=network['id'])
|
||||
return net_obj
|
||||
|
||||
def delete(self, context, network_uuid):
|
||||
"""Delete a network for client."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_fixed_ip(self, context, id):
|
||||
"""Get a fixed IP from the id."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def get_fixed_ip_by_address(self, context, address):
|
||||
"""Return instance uuids given an address."""
|
||||
uuid_maps = self._get_instance_uuids_by_ip(context, address)
|
||||
@ -2563,8 +2555,6 @@ class API(base_api.NetworkAPI):
|
||||
"""Return floating IP pool names."""
|
||||
client = get_client(context)
|
||||
pools = self._get_floating_ip_pools(client)
|
||||
# Note(salv-orlando): Return a list of names to be consistent with
|
||||
# nova.network.api.get_floating_ip_pools
|
||||
return [n['name'] or n['id'] for n in pools]
|
||||
|
||||
def _make_floating_ip_obj(self, context, fip, pool_dict, port_dict):
|
||||
@ -2639,9 +2629,6 @@ class API(base_api.NetworkAPI):
|
||||
return objects.VirtualInterfaceList.get_by_instance_uuid(context,
|
||||
instance.uuid)
|
||||
|
||||
def get_vif_by_mac_address(self, context, mac_address):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _get_floating_ip_pool_id_by_name_or_id(self, client, name_or_id):
|
||||
search_opts = {constants.NET_EXTERNAL: True, 'fields': 'id'}
|
||||
if uuidutils.is_uuid_like(name_or_id):
|
||||
@ -2660,27 +2647,10 @@ class API(base_api.NetworkAPI):
|
||||
% name_or_id)
|
||||
raise exception.NovaException(message=msg)
|
||||
|
||||
def _get_default_floating_ip_pool_name(self):
|
||||
"""Get default pool name from config.
|
||||
|
||||
TODO(stephenfin): Remove this helper function in Queens, opting to
|
||||
use the [neutron] option only.
|
||||
"""
|
||||
if CONF.default_floating_pool != 'nova':
|
||||
LOG.warning("Config option 'default_floating_pool' is set to "
|
||||
"a non-default value. Falling back to this value "
|
||||
"for now but this behavior will change in a "
|
||||
"future release. You should unset this value "
|
||||
"and set the '[neutron] default_floating_pool' "
|
||||
"option instead.")
|
||||
return CONF.default_floating_pool
|
||||
|
||||
return CONF.neutron.default_floating_pool
|
||||
|
||||
def allocate_floating_ip(self, context, pool=None):
|
||||
"""Add a floating IP to a project from a pool."""
|
||||
client = get_client(context)
|
||||
pool = pool or self._get_default_floating_ip_pool_name()
|
||||
pool = pool or CONF.neutron.default_floating_pool
|
||||
pool_id = self._get_floating_ip_pool_id_by_name_or_id(client, pool)
|
||||
|
||||
param = {'floatingip': {'floating_network_id': pool_id}}
|
||||
@ -3277,45 +3247,6 @@ class API(base_api.NetworkAPI):
|
||||
subnets.append(subnet_object)
|
||||
return subnets
|
||||
|
||||
def get_dns_domains(self, context):
|
||||
"""Return 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 private DNS domain with optional nova project."""
|
||||
raise NotImplementedError()
|
||||
|
||||
def setup_instance_network_on_host(
|
||||
self, context, instance, host, migration=None,
|
||||
provider_mappings=None):
|
||||
|
@ -1,47 +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.
|
||||
|
||||
|
||||
from nova.network import dns_driver
|
||||
|
||||
|
||||
class NoopDNSDriver(dns_driver.DNSDriver):
|
||||
"""No-op DNS manager. Does nothing."""
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def get_domains(self):
|
||||
return []
|
||||
|
||||
def create_entry(self, _name, _address, _type, _domain):
|
||||
pass
|
||||
|
||||
def delete_entry(self, _name, _domain):
|
||||
pass
|
||||
|
||||
def modify_address(self, _name, _address, _domain):
|
||||
pass
|
||||
|
||||
def get_entries_by_address(self, _address, _domain):
|
||||
return []
|
||||
|
||||
def get_entries_by_name(self, _name, _domain):
|
||||
return []
|
||||
|
||||
def create_domain(self, _fqdomain):
|
||||
pass
|
||||
|
||||
def delete_domain(self, _fqdomain):
|
||||
pass
|
@ -1,369 +0,0 @@
|
||||
# Copyright 2013, 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.
|
||||
|
||||
"""
|
||||
Client side of the network RPC API.
|
||||
"""
|
||||
|
||||
import oslo_messaging as messaging
|
||||
from oslo_serialization import jsonutils
|
||||
|
||||
import nova.conf
|
||||
from nova import exception
|
||||
from nova.objects import base as objects_base
|
||||
from nova import profiler
|
||||
from nova import rpc
|
||||
|
||||
|
||||
CONF = nova.conf.CONF
|
||||
RPC_TOPIC = 'network'
|
||||
|
||||
|
||||
@profiler.trace_cls("rpc")
|
||||
class NetworkAPI(object):
|
||||
'''Client side of the network rpc API.
|
||||
|
||||
API version history:
|
||||
|
||||
* 1.0 - Initial version.
|
||||
* 1.1 - Adds migrate_instance_[start|finish]
|
||||
* 1.2 - Make migrate_instance_[start|finish] a little more flexible
|
||||
* 1.3 - Adds fanout cast update_dns for multi_host networks
|
||||
* 1.4 - Add get_backdoor_port()
|
||||
* 1.5 - Adds associate
|
||||
* 1.6 - Adds instance_uuid to _{dis,}associate_floating_ip
|
||||
* 1.7 - Adds method get_floating_ip_pools to replace get_floating_pools
|
||||
* 1.8 - Adds macs to allocate_for_instance
|
||||
* 1.9 - Adds rxtx_factor to [add|remove]_fixed_ip, removes
|
||||
instance_uuid from allocate_for_instance and
|
||||
instance_get_nw_info
|
||||
|
||||
... Grizzly supports message version 1.9. So, any changes to existing
|
||||
methods in 1.x after that point should be done such that they can
|
||||
handle the version_cap being set to 1.9.
|
||||
|
||||
* 1.10- Adds (optional) requested_networks to deallocate_for_instance
|
||||
|
||||
... Havana supports message version 1.10. So, any changes to existing
|
||||
methods in 1.x after that point should be done such that they can
|
||||
handle the version_cap being set to 1.10.
|
||||
|
||||
* NOTE: remove unused method get_vifs_by_instance()
|
||||
* NOTE: remove unused method get_vif_by_mac_address()
|
||||
* NOTE: remove unused method get_network()
|
||||
* NOTE: remove unused method get_all_networks()
|
||||
* 1.11 - Add instance to deallocate_for_instance().
|
||||
Remove instance_id, project_id, and host.
|
||||
* 1.12 - Add instance to deallocate_fixed_ip()
|
||||
|
||||
... Icehouse supports message version 1.12. So, any changes to
|
||||
existing methods in 1.x after that point should be done such that they
|
||||
can handle the version_cap being set to 1.12.
|
||||
|
||||
* 1.13 - Convert allocate_for_instance()
|
||||
to use NetworkRequestList objects
|
||||
|
||||
... Juno and Kilo supports message version 1.13. So, any changes to
|
||||
existing methods in 1.x after that point should be done such that they
|
||||
can handle the version_cap being set to 1.13.
|
||||
|
||||
* NOTE: remove unused method get_floating_ips_by_fixed_address()
|
||||
* NOTE: remove unused method get_instance_uuids_by_ip_filter()
|
||||
* NOTE: remove unused method disassociate_network()
|
||||
* NOTE: remove unused method get_fixed_ip()
|
||||
* NOTE: remove unused method get_fixed_ip_by_address()
|
||||
* NOTE: remove unused method get_floating_ip()
|
||||
* NOTE: remove unused method get_floating_ip_pools()
|
||||
* NOTE: remove unused method get_floating_ip_by_address()
|
||||
* NOTE: remove unused method get_floating_ips_by_project()
|
||||
* NOTE: remove unused method get_instance_id_by_floating_address()
|
||||
* NOTE: remove unused method allocate_floating_ip()
|
||||
* NOTE: remove unused method deallocate_floating_ip()
|
||||
* NOTE: remove unused method associate_floating_ip()
|
||||
* NOTE: remove unused method disassociate_floating_ip()
|
||||
* NOTE: remove unused method associate()
|
||||
|
||||
* 1.14 - Add mac parameter to release_fixed_ip().
|
||||
* 1.15 - Convert set_network_host() to use Network objects.
|
||||
|
||||
... Liberty supports message version 1.15. So, any changes to
|
||||
existing methods in 1.x after that point should be done such that they
|
||||
can handle the version_cap being set to 1.15.
|
||||
|
||||
* 1.16 - Transfer instance in addition to instance_id in
|
||||
setup_networks_on_host
|
||||
|
||||
... Mitaka supports message version 1.16. So, any changes to
|
||||
existing methods in 1.x after that point should be done such that they
|
||||
can handle the version_cap being set to 1.16.
|
||||
|
||||
* 1.17 - Add method release_dhcp()
|
||||
|
||||
... Newton and Ocata support message version 1.17. So, any changes to
|
||||
existing methods in 1.x after that point should be done such that they
|
||||
can handle the version_cap being set to 1.17.
|
||||
'''
|
||||
|
||||
VERSION_ALIASES = {
|
||||
'grizzly': '1.9',
|
||||
'havana': '1.10',
|
||||
'icehouse': '1.12',
|
||||
'juno': '1.13',
|
||||
'kilo': '1.13',
|
||||
'liberty': '1.15',
|
||||
'mitaka': '1.16',
|
||||
'newton': '1.17',
|
||||
'ocata': '1.17',
|
||||
}
|
||||
|
||||
def __init__(self, topic=None):
|
||||
super(NetworkAPI, self).__init__()
|
||||
topic = topic or RPC_TOPIC
|
||||
target = messaging.Target(topic=topic, version='1.0')
|
||||
version_cap = self.VERSION_ALIASES.get(CONF.upgrade_levels.network,
|
||||
CONF.upgrade_levels.network)
|
||||
serializer = objects_base.NovaObjectSerializer()
|
||||
self.client = rpc.get_client(target, version_cap, serializer)
|
||||
|
||||
# TODO(russellb): Convert this to named arguments. It's a pretty large
|
||||
# list, so unwinding it all is probably best done in its own patch so it's
|
||||
# easier to review.
|
||||
def create_networks(self, ctxt, **kwargs):
|
||||
return self.client.call(ctxt, 'create_networks', **kwargs)
|
||||
|
||||
def delete_network(self, ctxt, uuid, fixed_range):
|
||||
return self.client.call(ctxt, 'delete_network',
|
||||
uuid=uuid, fixed_range=fixed_range)
|
||||
|
||||
def allocate_for_instance(self, ctxt, instance_id, project_id, host,
|
||||
rxtx_factor, vpn, requested_networks, macs=None,
|
||||
dhcp_options=None):
|
||||
# NOTE(mriedem): dhcp_options should be removed in version 2.0
|
||||
version = '1.13'
|
||||
if not self.client.can_send_version(version):
|
||||
version = '1.9'
|
||||
if requested_networks:
|
||||
requested_networks = requested_networks.as_tuples()
|
||||
|
||||
if CONF.multi_host:
|
||||
cctxt = self.client.prepare(version=version, server=host)
|
||||
else:
|
||||
cctxt = self.client.prepare(version=version)
|
||||
return cctxt.call(ctxt, 'allocate_for_instance',
|
||||
instance_id=instance_id, project_id=project_id,
|
||||
host=host, rxtx_factor=rxtx_factor, vpn=vpn,
|
||||
requested_networks=requested_networks,
|
||||
macs=jsonutils.to_primitive(macs))
|
||||
|
||||
def deallocate_for_instance(self, ctxt, instance, requested_networks=None):
|
||||
cctxt = self.client
|
||||
kwargs = {}
|
||||
if self.client.can_send_version('1.11'):
|
||||
version = '1.11'
|
||||
kwargs['instance'] = instance
|
||||
kwargs['requested_networks'] = requested_networks
|
||||
else:
|
||||
if self.client.can_send_version('1.10'):
|
||||
version = '1.10'
|
||||
kwargs['requested_networks'] = requested_networks
|
||||
else:
|
||||
version = '1.0'
|
||||
kwargs['host'] = instance.host
|
||||
kwargs['instance_id'] = instance.uuid
|
||||
kwargs['project_id'] = instance.project_id
|
||||
if CONF.multi_host:
|
||||
cctxt = cctxt.prepare(server=instance.host, version=version)
|
||||
return cctxt.call(ctxt, 'deallocate_for_instance', **kwargs)
|
||||
|
||||
def release_dhcp(self, ctxt, host, dev, address, vif_address):
|
||||
if self.client.can_send_version('1.17'):
|
||||
cctxt = self.client.prepare(version='1.17', server=host)
|
||||
return cctxt.call(ctxt, 'release_dhcp', dev=dev, address=address,
|
||||
vif_address=vif_address)
|
||||
else:
|
||||
raise exception.RPCPinnedToOldVersion()
|
||||
|
||||
def add_fixed_ip_to_instance(self, ctxt, instance_id, rxtx_factor,
|
||||
host, network_id):
|
||||
cctxt = self.client.prepare(version='1.9')
|
||||
return cctxt.call(ctxt, 'add_fixed_ip_to_instance',
|
||||
instance_id=instance_id, rxtx_factor=rxtx_factor,
|
||||
host=host, network_id=network_id)
|
||||
|
||||
def remove_fixed_ip_from_instance(self, ctxt, instance_id, rxtx_factor,
|
||||
host, address):
|
||||
cctxt = self.client.prepare(version='1.9')
|
||||
return cctxt.call(ctxt, 'remove_fixed_ip_from_instance',
|
||||
instance_id=instance_id, rxtx_factor=rxtx_factor,
|
||||
host=host, address=address)
|
||||
|
||||
def get_instance_nw_info(self, ctxt, instance_id, rxtx_factor, host,
|
||||
project_id):
|
||||
cctxt = self.client.prepare(version='1.9')
|
||||
return cctxt.call(ctxt, 'get_instance_nw_info',
|
||||
instance_id=instance_id, rxtx_factor=rxtx_factor,
|
||||
host=host, project_id=project_id)
|
||||
|
||||
def validate_networks(self, ctxt, networks):
|
||||
return self.client.call(ctxt, 'validate_networks', networks=networks)
|
||||
|
||||
def get_dns_domains(self, ctxt):
|
||||
return self.client.call(ctxt, 'get_dns_domains')
|
||||
|
||||
def add_dns_entry(self, ctxt, address, name, dns_type, domain):
|
||||
return self.client.call(ctxt, 'add_dns_entry',
|
||||
address=address, name=name,
|
||||
dns_type=dns_type, domain=domain)
|
||||
|
||||
def modify_dns_entry(self, ctxt, address, name, domain):
|
||||
return self.client.call(ctxt, 'modify_dns_entry',
|
||||
address=address, name=name, domain=domain)
|
||||
|
||||
def delete_dns_entry(self, ctxt, name, domain):
|
||||
return self.client.call(ctxt, 'delete_dns_entry',
|
||||
name=name, domain=domain)
|
||||
|
||||
def delete_dns_domain(self, ctxt, domain):
|
||||
return self.client.call(ctxt, 'delete_dns_domain', domain=domain)
|
||||
|
||||
def get_dns_entries_by_address(self, ctxt, address, domain):
|
||||
return self.client.call(ctxt, 'get_dns_entries_by_address',
|
||||
address=address, domain=domain)
|
||||
|
||||
def get_dns_entries_by_name(self, ctxt, name, domain):
|
||||
return self.client.call(ctxt, 'get_dns_entries_by_name',
|
||||
name=name, domain=domain)
|
||||
|
||||
def create_private_dns_domain(self, ctxt, domain, av_zone):
|
||||
return self.client.call(ctxt, 'create_private_dns_domain',
|
||||
domain=domain, av_zone=av_zone)
|
||||
|
||||
def create_public_dns_domain(self, ctxt, domain, project):
|
||||
return self.client.call(ctxt, 'create_public_dns_domain',
|
||||
domain=domain, project=project)
|
||||
|
||||
def setup_networks_on_host(self, ctxt, instance_id, host, teardown,
|
||||
instance):
|
||||
# NOTE(tr3buchet): the call is just to wait for completion
|
||||
version = '1.16'
|
||||
kwargs = {}
|
||||
if not self.client.can_send_version(version):
|
||||
version = '1.0'
|
||||
else:
|
||||
kwargs['instance'] = instance
|
||||
cctxt = self.client.prepare(version=version)
|
||||
return cctxt.call(ctxt, 'setup_networks_on_host',
|
||||
instance_id=instance_id, host=host,
|
||||
teardown=teardown, **kwargs)
|
||||
|
||||
def set_network_host(self, ctxt, network_ref):
|
||||
version = '1.15'
|
||||
if not self.client.can_send_version(version):
|
||||
version = '1.0'
|
||||
network_ref = objects_base.obj_to_primitive(network_ref)
|
||||
cctxt = self.client.prepare(version=version)
|
||||
return cctxt.call(ctxt, 'set_network_host', network_ref=network_ref)
|
||||
|
||||
def rpc_setup_network_on_host(self, ctxt, network_id, teardown, host):
|
||||
# NOTE(tr3buchet): the call is just to wait for completion
|
||||
cctxt = self.client.prepare(server=host)
|
||||
return cctxt.call(ctxt, 'rpc_setup_network_on_host',
|
||||
network_id=network_id, teardown=teardown)
|
||||
|
||||
# NOTE(russellb): Ideally this would not have a prefix of '_' since it is
|
||||
# a part of the rpc API. However, this is how it was being called when the
|
||||
# 1.0 API was being documented using this client proxy class. It should be
|
||||
# changed if there was ever a 2.0.
|
||||
def _rpc_allocate_fixed_ip(self, ctxt, instance_id, network_id, address,
|
||||
vpn, host):
|
||||
cctxt = self.client.prepare(server=host)
|
||||
return cctxt.call(ctxt, '_rpc_allocate_fixed_ip',
|
||||
instance_id=instance_id, network_id=network_id,
|
||||
address=address, vpn=vpn)
|
||||
|
||||
def deallocate_fixed_ip(self, ctxt, address, host, instance):
|
||||
kwargs = {}
|
||||
if self.client.can_send_version('1.12'):
|
||||
version = '1.12'
|
||||
kwargs['instance'] = instance
|
||||
else:
|
||||
version = '1.0'
|
||||
cctxt = self.client.prepare(server=host, version=version)
|
||||
return cctxt.call(ctxt, 'deallocate_fixed_ip',
|
||||
address=address, host=host, **kwargs)
|
||||
|
||||
def update_dns(self, ctxt, network_ids):
|
||||
cctxt = self.client.prepare(fanout=True, version='1.3')
|
||||
cctxt.cast(ctxt, 'update_dns', network_ids=network_ids)
|
||||
|
||||
# NOTE(russellb): Ideally this would not have a prefix of '_' since it is
|
||||
# a part of the rpc API. However, this is how it was being called when the
|
||||
# 1.0 API was being documented using this client proxy class. It should be
|
||||
# changed if there was ever a 2.0.
|
||||
def _associate_floating_ip(self, ctxt, floating_address, fixed_address,
|
||||
interface, host, instance_uuid=None):
|
||||
cctxt = self.client.prepare(server=host, version='1.6')
|
||||
return cctxt.call(ctxt, '_associate_floating_ip',
|
||||
floating_address=floating_address,
|
||||
fixed_address=fixed_address,
|
||||
interface=interface, instance_uuid=instance_uuid)
|
||||
|
||||
# NOTE(russellb): Ideally this would not have a prefix of '_' since it is
|
||||
# a part of the rpc API. However, this is how it was being called when the
|
||||
# 1.0 API was being documented using this client proxy class. It should be
|
||||
# changed if there was ever a 2.0.
|
||||
def _disassociate_floating_ip(self, ctxt, address, interface, host,
|
||||
instance_uuid=None):
|
||||
cctxt = self.client.prepare(server=host, version='1.6')
|
||||
return cctxt.call(ctxt, '_disassociate_floating_ip',
|
||||
address=address, interface=interface,
|
||||
instance_uuid=instance_uuid)
|
||||
|
||||
def lease_fixed_ip(self, ctxt, address, host):
|
||||
cctxt = self.client.prepare(server=host)
|
||||
cctxt.cast(ctxt, 'lease_fixed_ip', address=address)
|
||||
|
||||
def release_fixed_ip(self, ctxt, address, host, mac):
|
||||
kwargs = {}
|
||||
if self.client.can_send_version('1.14'):
|
||||
version = '1.14'
|
||||
kwargs['mac'] = mac
|
||||
else:
|
||||
version = '1.0'
|
||||
cctxt = self.client.prepare(server=host, version=version)
|
||||
cctxt.cast(ctxt, 'release_fixed_ip', address=address, **kwargs)
|
||||
|
||||
def migrate_instance_start(self, ctxt, instance_uuid, rxtx_factor,
|
||||
project_id, source_compute, dest_compute,
|
||||
floating_addresses, host=None):
|
||||
cctxt = self.client.prepare(server=host, version='1.2')
|
||||
return cctxt.call(ctxt, 'migrate_instance_start',
|
||||
instance_uuid=instance_uuid,
|
||||
rxtx_factor=rxtx_factor,
|
||||
project_id=project_id,
|
||||
source=source_compute,
|
||||
dest=dest_compute,
|
||||
floating_addresses=floating_addresses)
|
||||
|
||||
def migrate_instance_finish(self, ctxt, instance_uuid, rxtx_factor,
|
||||
project_id, source_compute, dest_compute,
|
||||
floating_addresses, host=None):
|
||||
cctxt = self.client.prepare(server=host, version='1.2')
|
||||
return cctxt.call(ctxt, 'migrate_instance_finish',
|
||||
instance_uuid=instance_uuid,
|
||||
rxtx_factor=rxtx_factor,
|
||||
project_id=project_id,
|
||||
source=source_compute,
|
||||
dest=dest_compute,
|
||||
floating_addresses=floating_addresses)
|
@ -33,7 +33,6 @@ def register_all():
|
||||
__import__('nova.objects.compute_node')
|
||||
__import__('nova.objects.diagnostics')
|
||||
__import__('nova.objects.console_auth_token')
|
||||
__import__('nova.objects.dns_domain')
|
||||
__import__('nova.objects.ec2')
|
||||
__import__('nova.objects.external_event')
|
||||
__import__('nova.objects.fixed_ip')
|
||||
|
@ -1,72 +0,0 @@
|
||||
# Copyright (C) 2014, 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.
|
||||
|
||||
from nova.db import api as db
|
||||
from nova import objects
|
||||
from nova.objects import base
|
||||
from nova.objects import fields
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class DNSDomain(base.NovaPersistentObject, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
|
||||
fields = {
|
||||
'domain': fields.StringField(),
|
||||
'scope': fields.StringField(nullable=True),
|
||||
'availability_zone': fields.StringField(nullable=True),
|
||||
'project_id': fields.StringField(nullable=True),
|
||||
}
|
||||
|
||||
@staticmethod
|
||||
def _from_db_object(context, vif, db_vif):
|
||||
for field in vif.fields:
|
||||
setattr(vif, field, db_vif[field])
|
||||
vif._context = context
|
||||
vif.obj_reset_changes()
|
||||
return vif
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_by_domain(cls, context, domain):
|
||||
db_dnsd = db.dnsdomain_get(context, domain)
|
||||
if db_dnsd:
|
||||
return cls._from_db_object(context, cls(), db_dnsd)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def register_for_zone(cls, context, domain, zone):
|
||||
db.dnsdomain_register_for_zone(context, domain, zone)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def register_for_project(cls, context, domain, project):
|
||||
db.dnsdomain_register_for_project(context, domain, project)
|
||||
|
||||
@base.remotable_classmethod
|
||||
def delete_by_domain(cls, context, domain):
|
||||
db.dnsdomain_unregister(context, domain)
|
||||
|
||||
|
||||
@base.NovaObjectRegistry.register
|
||||
class DNSDomainList(base.ObjectListBase, base.NovaObject):
|
||||
# Version 1.0: Initial version
|
||||
VERSION = '1.0'
|
||||
fields = {
|
||||
'objects': fields.ListOfObjectsField('DNSDomain'),
|
||||
}
|
||||
|
||||
@base.remotable_classmethod
|
||||
def get_all(cls, context):
|
||||
db_domains = db.dnsdomain_get_all(context)
|
||||
return base.obj_make_list(context, cls(context), objects.DNSDomain,
|
||||
db_domains)
|
@ -105,10 +105,8 @@ class Network(obj_base.NovaPersistentObject, obj_base.NovaObject,
|
||||
db_value = db_network[field]
|
||||
if field == 'netmask_v6' and db_value is not None:
|
||||
db_value = network._convert_legacy_ipv6_netmask(db_value)
|
||||
if field == 'dhcp_server' and db_value is None:
|
||||
elif field == 'dhcp_server' and db_value is None:
|
||||
db_value = db_network['gateway']
|
||||
if field == 'share_address' and CONF.share_dhcp_address:
|
||||
db_value = CONF.share_dhcp_address
|
||||
|
||||
network[field] = db_value
|
||||
network._context = context
|
||||
|
@ -16,7 +16,6 @@ from oslo_utils import versionutils
|
||||
|
||||
from nova.objects import base as obj_base
|
||||
from nova.objects import fields
|
||||
from nova import utils
|
||||
|
||||
# These are special case enums for the auto-allocate scenario. 'none' means
|
||||
# do not allocate a network on server create. 'auto' means auto-allocate a
|
||||
@ -49,24 +48,15 @@ class NetworkRequest(obj_base.NovaObject):
|
||||
def obj_load_attr(self, attr):
|
||||
setattr(self, attr, None)
|
||||
|
||||
# TODO(stephenfin): Drop the two item tuple case when we drop it entirely
|
||||
def to_tuple(self):
|
||||
address = str(self.address) if self.address is not None else None
|
||||
if utils.is_neutron():
|
||||
return self.network_id, address, self.port_id, self.pci_request_id
|
||||
else:
|
||||
return self.network_id, address
|
||||
return self.network_id, address, self.port_id, self.pci_request_id
|
||||
|
||||
# TODO(stephenfin): Drop the two item tuple case when we drop it entirely
|
||||
@classmethod
|
||||
def from_tuple(cls, net_tuple):
|
||||
if len(net_tuple) == 4:
|
||||
network_id, address, port_id, pci_request_id = net_tuple
|
||||
return cls(network_id=network_id, address=address,
|
||||
port_id=port_id, pci_request_id=pci_request_id)
|
||||
else:
|
||||
network_id, address = net_tuple
|
||||
return cls(network_id=network_id, address=address)
|
||||
network_id, address, port_id, pci_request_id = net_tuple
|
||||
return cls(network_id=network_id, address=address, port_id=port_id,
|
||||
pci_request_id=pci_request_id)
|
||||
|
||||
@property
|
||||
def auto_allocate(self):
|
||||
|
@ -19,53 +19,17 @@ Linux network specific helpers.
|
||||
|
||||
|
||||
import os
|
||||
import six
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
from nova import exception
|
||||
import nova.privsep.linux_net
|
||||
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def add_bridge(bridge):
|
||||
"""Add a bridge.
|
||||
|
||||
:param bridge: the name of the bridge
|
||||
"""
|
||||
processutils.execute('brctl', 'addbr', bridge)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def delete_bridge(bridge):
|
||||
"""Delete a bridge.
|
||||
|
||||
:param bridge: the name of the bridge
|
||||
"""
|
||||
processutils.execute('brctl', 'delbr', bridge)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def bridge_setfd(bridge):
|
||||
processutils.execute('brctl', 'setfd', bridge, 0)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def bridge_disable_stp(bridge):
|
||||
processutils.execute('brctl', 'stp', bridge, 'off')
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def bridge_add_interface(bridge, interface):
|
||||
return processutils.execute('brctl', 'addif', bridge, interface,
|
||||
check_exit_code=False)
|
||||
|
||||
|
||||
def device_exists(device):
|
||||
"""Check if ethernet device exists."""
|
||||
return os.path.exists('/sys/class/net/%s' % device)
|
||||
@ -117,11 +81,6 @@ def _set_device_trust_inner(dev, vf_num, trusted):
|
||||
check_exit_code=[0, 2, 254])
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def set_device_disabled(dev):
|
||||
processutils.execute('ip', 'link', 'set', dev, 'down')
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def set_device_macaddr(dev, mac_addr, port_state=None):
|
||||
_set_device_macaddr_inner(dev, mac_addr, port_state=port_state)
|
||||
@ -146,80 +105,6 @@ def set_device_macaddr_and_vlan(dev, vf_num, mac_addr, vlan):
|
||||
check_exit_code=[0, 2, 254])
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def bind_ip(device, ip, scope_is_link=False):
|
||||
if not scope_is_link:
|
||||
processutils.execute('ip', 'addr', 'add', str(ip) + '/32',
|
||||
'dev', device, check_exit_code=[0, 2, 254])
|
||||
else:
|
||||
processutils.execute('ip', 'addr', 'add', str(ip) + '/32',
|
||||
'scope', 'link', 'dev', device,
|
||||
check_exit_code=[0, 2, 254])
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def unbind_ip(device, ip):
|
||||
processutils.execute('ip', 'addr', 'del', str(ip) + '/32',
|
||||
'dev', device, check_exit_code=[0, 2, 254])
|
||||
|
||||
|
||||
def lookup_ip(device):
|
||||
return processutils.execute('ip', 'addr', 'show', 'dev', device,
|
||||
'scope', 'global')
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def change_ip(device, ip):
|
||||
processutils.execute('ip', '-f', 'inet6', 'addr', 'change', ip,
|
||||
'dev', device)
|
||||
|
||||
|
||||
# TODO(mikal): this is horrid. The calling code takes arguments from an
|
||||
# interface list and just regurgitates them here. This isn't good enough,
|
||||
# but is outside the scope of the privsep transition. Mark it as bonkers and
|
||||
# hope we clean it up later.
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def address_command_deprecated(device, action, params):
|
||||
cmd = ['ip', 'addr', action]
|
||||
cmd.extend(params)
|
||||
cmd.extend(['dev', device])
|
||||
processutils.execute(*cmd, check_exit_code=[0, 2, 254])
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def dhcp_release(dev, address, mac_address):
|
||||
processutils.execute('dhcp_release', dev, address, mac_address)
|
||||
|
||||
|
||||
def routes_show(dev):
|
||||
# Format of output is:
|
||||
# 192.168.1.0/24 proto kernel scope link src 192.168.1.6
|
||||
return processutils.execute('ip', 'route', 'show', 'dev', dev)
|
||||
|
||||
|
||||
# TODO(mikal): this is horrid. The calling code takes arguments from a route
|
||||
# list and just regurgitates them into new routes. This isn't good enough,
|
||||
# but is outside the scope of the privsep transition. Mark it as bonkers and
|
||||
# hope we clean it up later.
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def route_add_deprecated(routes):
|
||||
processutils.execute('ip', 'route', 'add', *routes)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def route_delete(dev, route):
|
||||
processutils.execute('ip', 'route', 'del', route, 'dev', dev)
|
||||
|
||||
|
||||
# TODO(mikal): this is horrid. The calling code takes arguments from a route
|
||||
# list and just regurgitates them into new routes. This isn't good enough,
|
||||
# but is outside the scope of the privsep transition. Mark it as bonkers and
|
||||
# hope we clean it up later.
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def route_delete_deprecated(dev, routes):
|
||||
processutils.execute('ip', 'route', 'del', *routes)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def create_tap_dev(dev, mac_address=None, multiqueue=False):
|
||||
if not device_exists(dev):
|
||||
@ -245,175 +130,8 @@ def create_tap_dev(dev, mac_address=None, multiqueue=False):
|
||||
_set_device_enabled_inner(dev)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def send_arp_for_ip(ip, device, count):
|
||||
out, err = processutils.execute(
|
||||
'arping', '-U', ip, '-A', '-I', device, '-c', str(count),
|
||||
check_exit_code=False)
|
||||
|
||||
if err:
|
||||
LOG.debug('arping error for IP %s', ip)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def clean_conntrack(fixed_ip):
|
||||
try:
|
||||
processutils.execute('conntrack', '-D', '-r', fixed_ip,
|
||||
check_exit_code=[0, 1])
|
||||
except processutils.ProcessExecutionError:
|
||||
LOG.exception('Error deleting conntrack entries for %s', fixed_ip)
|
||||
|
||||
|
||||
def enable_ipv4_forwarding():
|
||||
if not ipv4_forwarding_check():
|
||||
_enable_ipv4_forwarding_inner()
|
||||
|
||||
|
||||
def ipv4_forwarding_check():
|
||||
with open('/proc/sys/net/ipv4/ip_forward', 'r') as f:
|
||||
return f.readline().strip() == '1'
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def _enable_ipv4_forwarding_inner():
|
||||
processutils.execute('sysctl', '-w', 'net.ipv4.ip_forward=1')
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def modify_ebtables(table, rule, insert_rule=True):
|
||||
cmd = ['ebtables', '--concurrent', '-t', table]
|
||||
if insert_rule:
|
||||
cmd.append('-I')
|
||||
else:
|
||||
cmd.append('-D')
|
||||
cmd.extend(rule)
|
||||
|
||||
processutils.execute(*cmd, check_exit_code=[0])
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def add_vlan(bridge_interface, interface, vlan_num):
|
||||
processutils.execute('ip', 'link', 'add', 'link', bridge_interface,
|
||||
'name', interface, 'type', 'vlan',
|
||||
'id', vlan_num, check_exit_code=[0, 2, 254])
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def iptables_get_rules(ipv4=True):
|
||||
if ipv4:
|
||||
cmd = 'iptables'
|
||||
else:
|
||||
cmd = 'ip6tables'
|
||||
|
||||
return processutils.execute('%s-save' % cmd, '-c', attempts=5)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def iptables_set_rules(rules, ipv4=True):
|
||||
if ipv4:
|
||||
cmd = 'iptables'
|
||||
else:
|
||||
cmd = 'ip6tables'
|
||||
|
||||
processutils.execute('%s-restore' % cmd, '-c',
|
||||
process_input=six.b('\n'.join(rules)),
|
||||
attempts=5)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def restart_dnsmasq(flag_file, network_ref, config_file, pid_path, opts_path,
|
||||
dhcp_lease_time, lease_max, conf_path, dhcp_bridge,
|
||||
dhcp_domain, dns_servers, hosts_path):
|
||||
_restart_dnsmasq_inner(flag_file, network_ref, config_file, pid_path,
|
||||
opts_path, dhcp_lease_time, lease_max, conf_path,
|
||||
dhcp_bridge, dhcp_domain, dns_servers, hosts_path)
|
||||
|
||||
|
||||
# NOTE(mikal): this is done like this to enable unit testing
|
||||
def _restart_dnsmasq_inner(flag_file, network_ref, config_file, pid_path,
|
||||
opts_path, dhcp_lease_time, lease_max, conf_path,
|
||||
dhcp_bridge, dhcp_domain, dns_servers, hosts_path):
|
||||
cmd = ['env',
|
||||
'CONFIG_FILE=%s' % flag_file,
|
||||
'NETWORK_ID=%s' % str(network_ref['id']),
|
||||
'dnsmasq',
|
||||
'--strict-order',
|
||||
'--bind-interfaces',
|
||||
'--conf-file=%s' % config_file,
|
||||
'--pid-file=%s' % pid_path,
|
||||
'--dhcp-optsfile=%s' % opts_path,
|
||||
'--listen-address=%s' % network_ref['dhcp_server'],
|
||||
'--except-interface=lo',
|
||||
'--dhcp-range=set:%s,%s,static,%s,%ss' %
|
||||
(network_ref['label'],
|
||||
network_ref['dhcp_start'],
|
||||
network_ref['netmask'],
|
||||
dhcp_lease_time),
|
||||
'--dhcp-lease-max=%s' % lease_max,
|
||||
'--dhcp-hostsfile=%s' % conf_path,
|
||||
'--dhcp-script=%s' % dhcp_bridge,
|
||||
'--no-hosts',
|
||||
'--leasefile-ro']
|
||||
|
||||
# dnsmasq currently gives an error for an empty domain,
|
||||
# rather than ignoring. So only specify it if defined.
|
||||
if dhcp_domain:
|
||||
cmd.append('--domain=%s' % dhcp_domain)
|
||||
|
||||
if dns_servers:
|
||||
cmd.append('--no-resolv')
|
||||
for dns_server in dns_servers:
|
||||
cmd.append('--server=%s' % dns_server)
|
||||
|
||||
if network_ref['multi_host']:
|
||||
cmd.append('--addn-hosts=%s' % hosts_path)
|
||||
|
||||
processutils.execute(*cmd)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def start_ra(conf_path, pid_path):
|
||||
cmd = ['radvd',
|
||||
'-C', '%s' % conf_path,
|
||||
'-p', '%s' % pid_path]
|
||||
processutils.execute(*cmd)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def ovs_plug(timeout, bridge, dev, mac_address):
|
||||
cmd = ['ovs-vsctl', '--timeout=%s' % timeout,
|
||||
'--', '--may-exist', 'add-port', bridge, dev,
|
||||
'--', 'set', 'Interface', dev, 'type=internal',
|
||||
'--', 'set', 'Interface', dev,
|
||||
'external-ids:iface-id=%s' % dev,
|
||||
'--', 'set', 'Interface', dev,
|
||||
'external-ids:iface-status=active',
|
||||
'--', 'set', 'Interface', dev,
|
||||
'external-ids:attached-mac=%s' % mac_address]
|
||||
try:
|
||||
processutils.execute(*cmd)
|
||||
except Exception as e:
|
||||
LOG.error('Unable to execute %(cmd)s. Exception: %(exception)s',
|
||||
{'cmd': cmd, 'exception': e})
|
||||
raise exception.OVSConfigurationFailure(inner_exception=e)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def ovs_drop_nondhcp(bridge, mac_address):
|
||||
processutils.execute(
|
||||
'ovs-ofctl', 'add-flow', bridge, 'priority=1,actions=drop')
|
||||
processutils.execute(
|
||||
'ovs-ofctl', 'add-flow', bridge,
|
||||
'udp,tp_dst=67,dl_dst=%s,priority=2,actions=normal' % mac_address)
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def ovs_unplug(timeout, bridge, dev):
|
||||
cmd = ['ovs-vsctl', '--timeout=%s' % timeout,
|
||||
'--', '--if-exists', 'del-port', bridge, dev]
|
||||
try:
|
||||
processutils.execute(*cmd)
|
||||
except Exception as e:
|
||||
LOG.error('Unable to execute %(cmd)s. Exception: %(exception)s',
|
||||
{'cmd': cmd, 'exception': e})
|
||||
raise exception.OVSConfigurationFailure(inner_exception=e)
|
||||
|
@ -29,8 +29,6 @@ import sys
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import excutils
|
||||
|
||||
import nova.privsep
|
||||
|
||||
# NOTE(mriedem): Avoid importing nova.utils since that can cause a circular
|
||||
# import with the privsep code. In fact, avoid importing anything outside
|
||||
# of nova/privsep/ if possible.
|
||||
@ -90,8 +88,3 @@ def supports_direct_io(dirpath):
|
||||
pass
|
||||
|
||||
return hasDirectIO
|
||||
|
||||
|
||||
@nova.privsep.sys_admin_pctxt.entrypoint
|
||||
def kill(pid, signal):
|
||||
os.kill(pid, signal)
|
||||
|
69
nova/test.py
69
nova/test.py
@ -48,16 +48,13 @@ from oslo_utils import timeutils
|
||||
from oslo_versionedobjects import fixture as ovo_fixture
|
||||
from oslotest import base
|
||||
from oslotest import mock_fixture
|
||||
from oslotest import moxstubout
|
||||
import six
|
||||
from six.moves import builtins
|
||||
import testtools
|
||||
|
||||
from nova.compute import rpcapi as compute_rpcapi
|
||||
from nova import context
|
||||
from nova.db import api as db
|
||||
from nova import exception
|
||||
from nova.network import manager as network_manager
|
||||
from nova.network.security_group import openstack_driver
|
||||
from nova import objects
|
||||
from nova.objects import base as objects_base
|
||||
@ -89,36 +86,6 @@ CELL1_NAME = 'cell1'
|
||||
nested = utils.nested_contexts
|
||||
|
||||
|
||||
class SampleNetworks(fixtures.Fixture):
|
||||
|
||||
"""Create sample networks in the database."""
|
||||
|
||||
def __init__(self, host=None):
|
||||
self.host = host
|
||||
|
||||
def setUp(self):
|
||||
super(SampleNetworks, self).setUp()
|
||||
ctxt = context.get_admin_context()
|
||||
network = network_manager.VlanManager(host=self.host)
|
||||
bridge_interface = CONF.flat_interface or CONF.vlan_interface
|
||||
network.create_networks(ctxt,
|
||||
label='test',
|
||||
cidr='10.0.0.0/8',
|
||||
multi_host=CONF.multi_host,
|
||||
num_networks=CONF.num_networks,
|
||||
network_size=CONF.network_size,
|
||||
cidr_v6=CONF.fixed_range_v6,
|
||||
gateway=CONF.gateway,
|
||||
gateway_v6=CONF.gateway_v6,
|
||||
bridge=CONF.flat_network_bridge,
|
||||
bridge_interface=bridge_interface,
|
||||
vpn_start=CONF.vpn_start,
|
||||
vlan_start=CONF.vlan_start,
|
||||
dns1=CONF.flat_network_dns)
|
||||
for net in db.network_get_all(ctxt):
|
||||
network.set_network_host(ctxt, net)
|
||||
|
||||
|
||||
class TestingException(Exception):
|
||||
pass
|
||||
|
||||
@ -257,28 +224,12 @@ class TestCase(base.BaseTestCase):
|
||||
|
||||
self.useFixture(ovo_fixture.StableObjectJsonFixture())
|
||||
|
||||
# NOTE(mnaser): All calls to utils.is_neutron() are cached in
|
||||
# nova.utils._IS_NEUTRON. We set it to None to avoid any
|
||||
# caching of that value.
|
||||
utils._IS_NEUTRON = None
|
||||
|
||||
# Reset the global QEMU version flag.
|
||||
images.QEMU_VERSION = None
|
||||
|
||||
# Reset the compute RPC API globals (mostly the _ROUTER).
|
||||
compute_rpcapi.reset_globals()
|
||||
|
||||
# TODO(takashin): Remove MoxStubout fixture
|
||||
# after removing tests which uses mox and are related to
|
||||
# nova-network in the following files.
|
||||
#
|
||||
# - nova/tests/unit/api/openstack/compute/test_floating_ips.py
|
||||
# - nova/tests/unit/api/openstack/compute/test_security_groups.py
|
||||
# - nova/tests/unit/fake_network.py
|
||||
# - nova/tests/unit/network/test_manager.py
|
||||
mox_fixture = self.useFixture(moxstubout.MoxStubout())
|
||||
self.mox = mox_fixture.mox
|
||||
self.stubs = mox_fixture.stubs
|
||||
self.addCleanup(self._clear_attrs)
|
||||
self.useFixture(fixtures.EnvironmentVariable('http_proxy'))
|
||||
self.policy = self.useFixture(policy_fixture.PolicyFixture())
|
||||
@ -363,9 +314,6 @@ class TestCase(base.BaseTestCase):
|
||||
Use the monkey patch fixture to replace a function for the
|
||||
duration of a test. Useful when you want to provide fake
|
||||
methods instead of mocks during testing.
|
||||
|
||||
This should be used instead of self.stubs.Set (which is based
|
||||
on mox) going forward.
|
||||
"""
|
||||
self.useFixture(fixtures.MonkeyPatch(old, new))
|
||||
|
||||
@ -747,12 +695,9 @@ class BaseHookTestCase(NoDBTestCase):
|
||||
class MatchType(object):
|
||||
"""Matches any instance of a specified type
|
||||
|
||||
The MatchType class is a helper for use with the
|
||||
mock.assert_called_with() method that lets you
|
||||
assert that a particular parameter has a specific
|
||||
data type. It enables strict check than the built
|
||||
in mock.ANY helper, and is the equivalent of the
|
||||
mox.IsA() function from the legacy mox library
|
||||
The MatchType class is a helper for use with the mock.assert_called_with()
|
||||
method that lets you assert that a particular parameter has a specific data
|
||||
type. It enables stricter checking than the built in mock.ANY helper.
|
||||
|
||||
Example usage could be:
|
||||
|
||||
@ -794,11 +739,9 @@ class MatchObjPrims(object):
|
||||
class ContainKeyValue(object):
|
||||
"""Checks whether a key/value pair is in a dict parameter.
|
||||
|
||||
The ContainKeyValue class is a helper for use with the
|
||||
mock.assert_*() method that lets you assert that a particular
|
||||
dict contain a key/value pair. It enables strict check than
|
||||
the built in mock.ANY helper, and is the equivalent of the
|
||||
mox.ContainsKeyValue() function from the legacy mox library
|
||||
The ContainKeyValue class is a helper for use with the mock.assert_*()
|
||||
method that lets you assert that a particular dict contain a key/value
|
||||
pair. It enables stricter checking than the built in mock.ANY helper.
|
||||
|
||||
Example usage could be:
|
||||
|
||||
|
@ -96,11 +96,6 @@ class ServiceFixture(fixtures.Fixture):
|
||||
if self.cell:
|
||||
context.set_target_cell(self.ctxt, self.cell)
|
||||
|
||||
# NOTE(mikal): we don't have root to manipulate iptables, so just
|
||||
# zero that bit out.
|
||||
self.useFixture(fixtures.MockPatch(
|
||||
'nova.network.linux_net.IptablesManager._apply'))
|
||||
|
||||
with mock.patch('nova.context.get_admin_context',
|
||||
return_value=self.ctxt):
|
||||
self.service = service.Service.create(**self.kwargs)
|
||||
@ -791,18 +786,6 @@ class WarningsFixture(fixtures.Fixture):
|
||||
message='Policy enforcement is depending on the value of is_admin.'
|
||||
' This key is deprecated. Please update your policy '
|
||||
'file to use the standard policy values.')
|
||||
# TODO(takashin): Remove filtering warnings about mox
|
||||
# after removing tests which uses mox and are related to
|
||||
# nova-network in the following files.
|
||||
#
|
||||
# - nova/tests/unit/api/openstack/compute/test_floating_ips.py
|
||||
# - nova/tests/unit/api/openstack/compute/test_security_groups.py
|
||||
# - nova/tests/unit/fake_network.py
|
||||
# - nova/tests/unit/network/test_manager.py
|
||||
warnings.filterwarnings('ignore',
|
||||
module='mox3.mox')
|
||||
# NOTE(gibi): we can remove this once we get rid of Mox in nova
|
||||
warnings.filterwarnings('ignore', message="Using class 'MoxStubout'")
|
||||
# NOTE(mriedem): Ignore scope check UserWarnings from oslo.policy.
|
||||
warnings.filterwarnings('ignore',
|
||||
message="Policy .* failed scope check",
|
||||
@ -990,12 +973,6 @@ class OSMetadataServer(fixtures.Fixture):
|
||||
}
|
||||
self.useFixture(ConfPatcher(**conf_overrides))
|
||||
|
||||
# NOTE(mikal): we don't have root to manipulate iptables, so just
|
||||
# zero that bit out.
|
||||
self.useFixture(fixtures.MonkeyPatch(
|
||||
'nova.network.linux_net.IptablesManager._apply',
|
||||
lambda _: None))
|
||||
|
||||
self.metadata = service.WSGIService("metadata")
|
||||
self.metadata.start()
|
||||
self.addCleanup(self.metadata.stop)
|
||||
|
@ -1,95 +0,0 @@
|
||||
=====================================
|
||||
OpenStack Nova Testing Infrastructure
|
||||
=====================================
|
||||
|
||||
This README file attempts to provide current and prospective contributors with
|
||||
everything they need to know in order to start creating unit tests for nova.
|
||||
|
||||
Note: the content for the rest of this file will be added as the work items in
|
||||
the following blueprint are completed:
|
||||
https://blueprints.launchpad.net/nova/+spec/consolidate-testing-infrastructure
|
||||
|
||||
|
||||
Test Types: Unit vs. Functional vs. Integration
|
||||
-----------------------------------------------
|
||||
|
||||
TBD
|
||||
|
||||
Writing Unit Tests
|
||||
------------------
|
||||
|
||||
TBD
|
||||
|
||||
Using Fakes
|
||||
~~~~~~~~~~~
|
||||
|
||||
TBD
|
||||
|
||||
test.TestCase
|
||||
-------------
|
||||
The TestCase class from nova.test (generally imported as test) will
|
||||
automatically manage self.stubs using the stubout module and self.mox
|
||||
using the mox module during the setUp step. They will automatically
|
||||
verify and clean up during the tearDown step.
|
||||
|
||||
If using test.TestCase, calling the super class setUp is required and
|
||||
calling the super class tearDown is required to be last if tearDown
|
||||
is overridden.
|
||||
|
||||
Writing Functional Tests
|
||||
------------------------
|
||||
|
||||
TBD
|
||||
|
||||
Writing Integration Tests
|
||||
-------------------------
|
||||
|
||||
TBD
|
||||
|
||||
Tests and Exceptions
|
||||
--------------------
|
||||
A properly written test asserts that particular behavior occurs. This can
|
||||
be a success condition or a failure condition, including an exception.
|
||||
When asserting that a particular exception is raised, the most specific
|
||||
exception possible should be used.
|
||||
|
||||
In particular, testing for Exception being raised is almost always a
|
||||
mistake since it will match (almost) every exception, even those
|
||||
unrelated to the exception intended to be tested.
|
||||
|
||||
This applies to catching exceptions manually with a try/except block,
|
||||
or using assertRaises().
|
||||
|
||||
Example::
|
||||
|
||||
self.assertRaises(exception.InstanceNotFound, db.instance_get_by_uuid,
|
||||
elevated, instance_uuid)
|
||||
|
||||
If a stubbed function/method needs a generic exception for testing
|
||||
purposes, test.TestingException is available.
|
||||
|
||||
Example::
|
||||
|
||||
def stubbed_method(self):
|
||||
raise test.TestingException()
|
||||
self.stubs.Set(cls, 'inner_method', stubbed_method)
|
||||
|
||||
obj = cls()
|
||||
self.assertRaises(test.TestingException, obj.outer_method)
|
||||
|
||||
|
||||
Stubbing and Mocking
|
||||
--------------------
|
||||
|
||||
Whenever possible, tests SHOULD NOT stub and mock out the same function.
|
||||
|
||||
If it's unavoidable, tests SHOULD define stubs before mocks since the
|
||||
`TestCase` cleanup routine will un-mock before un-stubbing. Doing otherwise
|
||||
results in a test that leaks stubbed functions, causing hard-to-debug
|
||||
interference between tests [1]_.
|
||||
|
||||
If a mock must take place before a stub, any stubs after the mock call MUST be
|
||||
manually unset using `self.cleanUp` calls within the test.
|
||||
|
||||
|
||||
.. [1] https://bugs.launchpad.net/nova/+bug/1180671
|
@ -19,11 +19,7 @@ from oslo_utils.fixture import uuidsentinel as uuids
|
||||
import webob
|
||||
|
||||
from nova.api.openstack.compute import floating_ips as fips_v21
|
||||
from nova import compute
|
||||
from nova import context
|
||||
from nova.db import api as db
|
||||
from nova import exception
|
||||
from nova import network
|
||||
from nova import objects
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
@ -195,94 +191,6 @@ class FloatingIpTestV21(test.NoDBTestCase):
|
||||
expected_exc)
|
||||
|
||||
|
||||
class ExtendedFloatingIpTestV21(test.TestCase):
|
||||
floating_ip = "10.10.10.10"
|
||||
floating_ip_2 = "10.10.10.11"
|
||||
floating_ips = fips_v21
|
||||
|
||||
def _create_floating_ips(self, floating_ips=None):
|
||||
"""Create a floating IP object."""
|
||||
if floating_ips is None:
|
||||
floating_ips = [self.floating_ip]
|
||||
elif not isinstance(floating_ips, (list, tuple)):
|
||||
floating_ips = [floating_ips]
|
||||
|
||||
dict_ = {'pool': 'nova', 'host': 'fake_host'}
|
||||
return db.floating_ip_bulk_create(
|
||||
self.context, [dict(address=ip, **dict_) for ip in floating_ips],
|
||||
)
|
||||
|
||||
def _delete_floating_ip(self):
|
||||
db.floating_ip_destroy(self.context, self.floating_ip)
|
||||
|
||||
def setUp(self):
|
||||
super(ExtendedFloatingIpTestV21, self).setUp()
|
||||
self.stubs.Set(compute.api.API, "get",
|
||||
compute_api_get)
|
||||
self.stubs.Set(network.api.API, "get_floating_ip",
|
||||
network_api_get_floating_ip)
|
||||
self.stubs.Set(network.api.API, "get_floating_ip_by_address",
|
||||
network_api_get_floating_ip_by_address)
|
||||
self.stubs.Set(network.api.API, "get_floating_ips_by_project",
|
||||
network_api_get_floating_ips_by_project)
|
||||
self.stubs.Set(network.api.API, "release_floating_ip",
|
||||
network_api_release)
|
||||
self.stubs.Set(network.api.API, "disassociate_floating_ip",
|
||||
network_api_disassociate)
|
||||
self.stubs.Set(network.api.API, "get_instance_id_by_floating_address",
|
||||
get_instance_by_floating_ip_addr)
|
||||
self.stubs.Set(objects.Instance, "get_network_info",
|
||||
stub_nw_info(self))
|
||||
|
||||
fake_network.stub_out_nw_api_get_instance_nw_info(self)
|
||||
self.stub_out('nova.db.api.instance_get',
|
||||
fake_instance_get)
|
||||
|
||||
self.context = context.get_admin_context()
|
||||
self._create_floating_ips()
|
||||
|
||||
self.controller = self.floating_ips.FloatingIPController()
|
||||
self.manager = self.floating_ips.\
|
||||
FloatingIPActionController()
|
||||
self.fake_req = fakes.HTTPRequest.blank('')
|
||||
|
||||
def tearDown(self):
|
||||
self._delete_floating_ip()
|
||||
super(ExtendedFloatingIpTestV21, self).tearDown()
|
||||
|
||||
def test_extended_floating_ip_associate_fixed(self):
|
||||
fixed_address = '192.168.1.100'
|
||||
|
||||
def fake_associate_floating_ip(*args, **kwargs):
|
||||
self.assertEqual(fixed_address, kwargs['fixed_address'])
|
||||
|
||||
body = dict(addFloatingIp=dict(address=self.floating_ip,
|
||||
fixed_address=fixed_address))
|
||||
|
||||
with mock.patch.object(self.manager.network_api,
|
||||
'associate_floating_ip',
|
||||
fake_associate_floating_ip):
|
||||
rsp = self.manager._add_floating_ip(self.fake_req, TEST_INST,
|
||||
body=body)
|
||||
self.assertEqual(202, rsp.status_int)
|
||||
|
||||
def test_extended_floating_ip_associate_fixed_not_allocated(self):
|
||||
def fake_associate_floating_ip(*args, **kwargs):
|
||||
pass
|
||||
|
||||
self.stubs.Set(network.api.API, "associate_floating_ip",
|
||||
fake_associate_floating_ip)
|
||||
body = dict(addFloatingIp=dict(address=self.floating_ip,
|
||||
fixed_address='11.11.11.11'))
|
||||
|
||||
ex = self.assertRaises(webob.exc.HTTPBadRequest,
|
||||
self.manager._add_floating_ip,
|
||||
self.fake_req, TEST_INST, body=body)
|
||||
|
||||
self.assertIn("Specified fixed address not assigned to instance",
|
||||
ex.explanation)
|
||||
|
||||
|
||||
class FloatingIPPolicyEnforcementV21(test.NoDBTestCase):
|
||||
|
||||
def setUp(self):
|
||||
|
@ -20,8 +20,6 @@ from oslo_policy import opts as policy_opts
|
||||
from nova.conf import neutron
|
||||
from nova.conf import paths
|
||||
from nova import config
|
||||
from nova import ipv6
|
||||
from nova.tests.unit import utils
|
||||
|
||||
|
||||
class ConfFixture(config_fixture.Config):
|
||||
@ -31,19 +29,8 @@ class ConfFixture(config_fixture.Config):
|
||||
|
||||
# default group
|
||||
self.conf.set_default('compute_driver', 'fake.SmallFakeDriver')
|
||||
self.conf.set_default('fake_network', True)
|
||||
self.conf.set_default('flat_network_bridge', 'br100')
|
||||
self.conf.set_default('floating_ip_dns_manager',
|
||||
'nova.tests.unit.utils.dns_manager')
|
||||
self.conf.set_default('force_dhcp_release', False)
|
||||
self.conf.set_default('host', 'fake-mini')
|
||||
self.conf.set_default('instance_dns_manager',
|
||||
'nova.tests.unit.utils.dns_manager')
|
||||
self.conf.set_default('network_size', 8)
|
||||
self.conf.set_default('num_networks', 2)
|
||||
self.conf.set_default('periodic_enable', False)
|
||||
self.conf.set_default('use_ipv6', True)
|
||||
self.conf.set_default('vlan_interface', 'eth0')
|
||||
|
||||
# api_database group
|
||||
self.conf.set_default('connection', "sqlite://", group='api_database')
|
||||
@ -77,5 +64,3 @@ class ConfFixture(config_fixture.Config):
|
||||
init_rpc=False)
|
||||
policy_opts.set_defaults(self.conf)
|
||||
neutron.register_dynamic_opts(self.conf)
|
||||
self.addCleanup(utils.cleanup_dns_managers)
|
||||
self.addCleanup(ipv6.api.reset_backend)
|
||||
|
@ -7854,44 +7854,6 @@ class CertificateTestCase(test.TestCase, ModelsObjectComparatorMixin):
|
||||
self._assertEqualObjects(self.created[1], cert[0])
|
||||
|
||||
|
||||
class DnsdomainTestCase(test.TestCase):
|
||||
|
||||
def setUp(self):
|
||||
super(DnsdomainTestCase, self).setUp()
|
||||
self.ctxt = context.get_admin_context()
|
||||
self.domain = 'test.domain'
|
||||
self.testzone = 'testzone'
|
||||
self.project = 'fake'
|
||||
|
||||
def test_dnsdomain_register_for_zone(self):
|
||||
db.dnsdomain_register_for_zone(self.ctxt, self.domain, self.testzone)
|
||||
domain = db.dnsdomain_get(self.ctxt, self.domain)
|
||||
self.assertEqual(domain['domain'], self.domain)
|
||||
self.assertEqual(domain['availability_zone'], self.testzone)
|
||||
self.assertEqual(domain['scope'], 'private')
|
||||
|
||||
def test_dnsdomain_register_for_project(self):
|
||||
db.dnsdomain_register_for_project(self.ctxt, self.domain, self.project)
|
||||
domain = db.dnsdomain_get(self.ctxt, self.domain)
|
||||
self.assertEqual(domain['domain'], self.domain)
|
||||
self.assertEqual(domain['project_id'], self.project)
|
||||
self.assertEqual(domain['scope'], 'public')
|
||||
|
||||
def test_dnsdomain_unregister(self):
|
||||
db.dnsdomain_register_for_zone(self.ctxt, self.domain, self.testzone)
|
||||
db.dnsdomain_unregister(self.ctxt, self.domain)
|
||||
domain = db.dnsdomain_get(self.ctxt, self.domain)
|
||||
self.assertIsNone(domain)
|
||||
|
||||
def test_dnsdomain_get_all(self):
|
||||
d_list = ['test.domain.one', 'test.domain.two']
|
||||
db.dnsdomain_register_for_zone(self.ctxt, d_list[0], 'zone')
|
||||
db.dnsdomain_register_for_zone(self.ctxt, d_list[1], 'zone')
|
||||
db_list = db.dnsdomain_get_all(self.ctxt)
|
||||
db_domain_list = [d.domain for d in db_list]
|
||||
self.assertEqual(sorted(d_list), sorted(db_domain_list))
|
||||
|
||||
|
||||
class BwUsageTestCase(test.TestCase, ModelsObjectComparatorMixin):
|
||||
|
||||
_ignored_keys = ['id', 'deleted', 'deleted_at', 'created_at', 'updated_at']
|
||||
@ -8189,9 +8151,6 @@ class ArchiveTestCase(test.TestCase, ModelsObjectComparatorMixin):
|
||||
self.instance_id_mappings = models.InstanceIdMapping.__table__
|
||||
self.shadow_instance_id_mappings = sqlalchemyutils.get_table(
|
||||
self.engine, "shadow_instance_id_mappings")
|
||||
self.dns_domains = models.DNSDomain.__table__
|
||||
self.shadow_dns_domains = sqlalchemyutils.get_table(
|
||||
self.engine, "shadow_dns_domains")
|
||||
self.instances = models.Instance.__table__
|
||||
self.shadow_instances = sqlalchemyutils.get_table(
|
||||
self.engine, "shadow_instances")
|
||||
@ -8473,31 +8432,6 @@ class ArchiveTestCase(test.TestCase, ModelsObjectComparatorMixin):
|
||||
self.assertEqual(len(rows), 4)
|
||||
return 0
|
||||
|
||||
def test_archive_deleted_rows_no_id_column(self):
|
||||
uuidstr0 = self.uuidstrs[0]
|
||||
ins_stmt = self.dns_domains.insert().values(domain=uuidstr0)
|
||||
self.conn.execute(ins_stmt)
|
||||
update_statement = self.dns_domains.update().\
|
||||
where(self.dns_domains.c.domain == uuidstr0).\
|
||||
values(deleted=True, deleted_at=timeutils.utcnow())
|
||||
self.conn.execute(update_statement)
|
||||
qdd = sql.select([self.dns_domains], self.dns_domains.c.domain ==
|
||||
uuidstr0)
|
||||
rows = self.conn.execute(qdd).fetchall()
|
||||
self.assertEqual(len(rows), 1)
|
||||
qsdd = sql.select([self.shadow_dns_domains],
|
||||
self.shadow_dns_domains.c.domain == uuidstr0)
|
||||
rows = self.conn.execute(qsdd).fetchall()
|
||||
self.assertEqual(len(rows), 0)
|
||||
db.archive_deleted_rows(max_rows=1)
|
||||
rows = self.conn.execute(qdd).fetchall()
|
||||
self.assertEqual(len(rows), 0)
|
||||
rows = self.conn.execute(qsdd).fetchall()
|
||||
self.assertEqual(len(rows), 1)
|
||||
self._assert_shadow_tables_empty_except(
|
||||
'shadow_dns_domains',
|
||||
)
|
||||
|
||||
def test_archive_deleted_rows_shadow_insertions_equals_deletions(self):
|
||||
# Add 2 rows to table
|
||||
for uuidstr in self.uuidstrs[:2]:
|
||||
|
@ -15,267 +15,16 @@
|
||||
|
||||
from oslo_serialization import jsonutils
|
||||
from oslo_utils.fixture import uuidsentinel as uuids
|
||||
from six.moves import range
|
||||
|
||||
from nova.compute import api as compute_api
|
||||
from nova.compute import manager as compute_manager
|
||||
import nova.conf
|
||||
import nova.context
|
||||
from nova.db import api as db
|
||||
from nova import exception
|
||||
from nova.network import manager as network_manager
|
||||
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.objects import network as network_obj
|
||||
from nova.objects import virtual_interface as vif_obj
|
||||
from nova.tests.unit.objects import test_fixed_ip
|
||||
from nova.tests.unit.objects import test_instance_info_cache
|
||||
from nova.tests.unit.objects import test_pci_device
|
||||
from nova.tests.unit import utils
|
||||
|
||||
|
||||
HOST = "testhost"
|
||||
CONF = nova.conf.CONF
|
||||
|
||||
|
||||
class FakeModel(dict):
|
||||
"""Represent a model from the db."""
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.update(kwargs)
|
||||
|
||||
|
||||
class FakeNetworkManager(network_manager.NetworkManager):
|
||||
"""This NetworkManager doesn't call the base class so we can bypass all
|
||||
inherited service cruft and just perform unit tests.
|
||||
"""
|
||||
|
||||
class FakeDB(object):
|
||||
vifs = [{'id': 0,
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'deleted': 0,
|
||||
'instance_uuid': uuids.instance_1,
|
||||
'network_id': 1,
|
||||
'uuid': uuids.vifs_1,
|
||||
'address': 'DC:AD:BE:FF:EF:01',
|
||||
'tag': 'fake-tag1'},
|
||||
{'id': 1,
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'deleted': 0,
|
||||
'instance_uuid': uuids.instance_2,
|
||||
'network_id': 21,
|
||||
'uuid': uuids.vifs_2,
|
||||
'address': 'DC:AD:BE:FF:EF:02',
|
||||
'tag': 'fake-tag2'},
|
||||
{'id': 2,
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'deleted': 0,
|
||||
'instance_uuid': uuids.instance_1,
|
||||
'network_id': 31,
|
||||
'uuid': uuids.vifs_3,
|
||||
'address': 'DC:AD:BE:FF:EF:03',
|
||||
'tag': None}]
|
||||
|
||||
floating_ips = [dict(address='172.16.1.1',
|
||||
fixed_ip_id=100),
|
||||
dict(address='172.16.1.2',
|
||||
fixed_ip_id=200),
|
||||
dict(address='173.16.1.2',
|
||||
fixed_ip_id=210)]
|
||||
|
||||
fixed_ips = [dict(test_fixed_ip.fake_fixed_ip,
|
||||
id=100,
|
||||
address='172.16.0.1',
|
||||
virtual_interface_id=0),
|
||||
dict(test_fixed_ip.fake_fixed_ip,
|
||||
id=200,
|
||||
address='172.16.0.2',
|
||||
virtual_interface_id=1),
|
||||
dict(test_fixed_ip.fake_fixed_ip,
|
||||
id=210,
|
||||
address='173.16.0.2',
|
||||
virtual_interface_id=2)]
|
||||
|
||||
def fixed_ip_get_by_instance(self, context, instance_uuid):
|
||||
return [dict(address='10.0.0.0'), dict(address='10.0.0.1'),
|
||||
dict(address='10.0.0.2')]
|
||||
|
||||
def network_get_by_cidr(self, context, cidr):
|
||||
raise exception.NetworkNotFoundForCidr(cidr=cidr)
|
||||
|
||||
def network_create_safe(self, context, net):
|
||||
fakenet = dict(net)
|
||||
fakenet['id'] = 999
|
||||
return fakenet
|
||||
|
||||
def network_get(self, context, network_id, project_only="allow_none"):
|
||||
return {'cidr_v6': '2001:db8:69:%x::/64' % network_id}
|
||||
|
||||
def network_get_by_uuid(self, context, network_uuid):
|
||||
raise exception.NetworkNotFoundForUUID(uuid=network_uuid)
|
||||
|
||||
def network_get_all(self, context):
|
||||
raise exception.NoNetworksFound()
|
||||
|
||||
def network_get_all_by_uuids(self, context, project_only="allow_none"):
|
||||
raise exception.NoNetworksFound()
|
||||
|
||||
def network_disassociate(self, context, network_id):
|
||||
return True
|
||||
|
||||
def virtual_interface_get_all(self, context):
|
||||
return self.vifs
|
||||
|
||||
def fixed_ips_by_virtual_interface(self, context, vif_id):
|
||||
return [ip for ip in self.fixed_ips
|
||||
if ip['virtual_interface_id'] == vif_id]
|
||||
|
||||
def fixed_ip_disassociate(self, context, address):
|
||||
return True
|
||||
|
||||
def __init__(self, stubs=None):
|
||||
self.db = self.FakeDB()
|
||||
if stubs:
|
||||
stubs.Set(vif_obj, 'db', self.db)
|
||||
self.deallocate_called = None
|
||||
self.deallocate_fixed_ip_calls = []
|
||||
self.network_rpcapi = network_rpcapi.NetworkAPI()
|
||||
|
||||
# TODO(matelakat) method signature should align with the faked one's
|
||||
def deallocate_fixed_ip(self, context, address=None, host=None,
|
||||
instance=None):
|
||||
self.deallocate_fixed_ip_calls.append((context, address, host))
|
||||
# TODO(matelakat) use the deallocate_fixed_ip_calls instead
|
||||
self.deallocate_called = address
|
||||
|
||||
def _create_fixed_ips(self, context, network_id, fixed_cidr=None,
|
||||
extra_reserved=None, bottom_reserved=0,
|
||||
top_reserved=0):
|
||||
pass
|
||||
|
||||
def get_instance_nw_info(context, instance_id, rxtx_factor,
|
||||
host, instance_uuid=None, **kwargs):
|
||||
pass
|
||||
|
||||
|
||||
def fake_network(network_id, ipv6=None):
|
||||
if ipv6 is None:
|
||||
ipv6 = CONF.use_ipv6
|
||||
fake_network = {'id': network_id,
|
||||
'uuid': getattr(uuids, 'network%i' % network_id),
|
||||
'label': 'test%d' % network_id,
|
||||
'injected': False,
|
||||
'multi_host': False,
|
||||
'cidr': '192.168.%d.0/24' % network_id,
|
||||
'cidr_v6': None,
|
||||
'netmask': '255.255.255.0',
|
||||
'netmask_v6': None,
|
||||
'bridge': 'fake_br%d' % network_id,
|
||||
'bridge_interface': 'fake_eth%d' % network_id,
|
||||
'gateway': '192.168.%d.1' % network_id,
|
||||
'gateway_v6': None,
|
||||
'broadcast': '192.168.%d.255' % network_id,
|
||||
'dns1': '192.168.%d.3' % network_id,
|
||||
'dns2': '192.168.%d.4' % network_id,
|
||||
'dns3': '192.168.%d.3' % network_id,
|
||||
'vlan': None,
|
||||
'host': None,
|
||||
'project_id': uuids.project,
|
||||
'vpn_public_address': '192.168.%d.2' % network_id,
|
||||
'vpn_public_port': None,
|
||||
'vpn_private_address': None,
|
||||
'dhcp_start': None,
|
||||
'rxtx_base': network_id * 10,
|
||||
'priority': None,
|
||||
'deleted': False,
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'mtu': None,
|
||||
'dhcp_server': '192.168.%d.1' % network_id,
|
||||
'enable_dhcp': True,
|
||||
'share_address': False}
|
||||
if ipv6:
|
||||
fake_network['cidr_v6'] = '2001:db8:0:%x::/64' % network_id
|
||||
fake_network['gateway_v6'] = '2001:db8:0:%x::1' % network_id
|
||||
fake_network['netmask_v6'] = '64'
|
||||
if CONF.flat_injected:
|
||||
fake_network['injected'] = True
|
||||
|
||||
return fake_network
|
||||
|
||||
|
||||
def fake_network_obj(context, network_id=1, ipv6=None):
|
||||
return network_obj.Network._from_db_object(
|
||||
context, network_obj.Network(), fake_network(network_id, ipv6))
|
||||
|
||||
|
||||
def fake_vif(x):
|
||||
return {'id': x,
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'deleted': 0,
|
||||
'address': 'DE:AD:BE:EF:00:%02x' % x,
|
||||
'uuid': getattr(uuids, 'vif%i' % x),
|
||||
'network_id': x,
|
||||
'instance_uuid': uuids.vifs_1,
|
||||
'tag': 'fake-tag'}
|
||||
|
||||
|
||||
def floating_ip_ids():
|
||||
for i in range(1, 100):
|
||||
yield i
|
||||
|
||||
|
||||
def fixed_ip_ids():
|
||||
for i in range(1, 100):
|
||||
yield i
|
||||
|
||||
|
||||
floating_ip_id = floating_ip_ids()
|
||||
fixed_ip_id = fixed_ip_ids()
|
||||
|
||||
|
||||
def next_fixed_ip(network_id, num_floating_ips=0):
|
||||
next_id = next(fixed_ip_id)
|
||||
f_ips = [FakeModel(**next_floating_ip(next_id))
|
||||
for i in range(num_floating_ips)]
|
||||
return {'id': next_id,
|
||||
'network_id': network_id,
|
||||
'address': '192.168.%d.%03d' % (network_id, (next_id + 99)),
|
||||
'instance_uuid': uuids.fixed_ip,
|
||||
'allocated': False,
|
||||
'reserved': False,
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'leased': True,
|
||||
'host': HOST,
|
||||
'deleted': 0,
|
||||
'network': fake_network(network_id),
|
||||
'virtual_interface': fake_vif(network_id),
|
||||
# and since network_id and vif_id happen to be equivalent
|
||||
'virtual_interface_id': network_id,
|
||||
'floating_ips': f_ips}
|
||||
|
||||
|
||||
def next_floating_ip(fixed_ip_id):
|
||||
next_id = next(floating_ip_id)
|
||||
return {'id': next_id,
|
||||
'address': '10.10.10.%03d' % (next_id + 99),
|
||||
'fixed_ip_id': fixed_ip_id,
|
||||
'project_id': None,
|
||||
'auto_assigned': False}
|
||||
|
||||
|
||||
def fake_get_instance_nw_info(test, num_networks=1):
|
||||
|
||||
def update_cache_fake(*args, **kwargs):
|
||||
@ -380,16 +129,6 @@ def fake_get_instance_nw_info(test, num_networks=1):
|
||||
return nw_model
|
||||
|
||||
|
||||
def stub_out_nw_api_get_instance_nw_info(test, func=None):
|
||||
|
||||
def get_instance_nw_info(self, context, instance, conductor_api=None):
|
||||
return fake_get_instance_nw_info(test)
|
||||
|
||||
if func is None:
|
||||
func = get_instance_nw_info
|
||||
test.stub_out('nova.network.api.API.get_instance_nw_info', func)
|
||||
|
||||
|
||||
_real_functions = {}
|
||||
|
||||
|
||||
@ -421,30 +160,6 @@ def unset_stub_network_methods(test):
|
||||
_real_functions[name])
|
||||
|
||||
|
||||
def stub_compute_with_ips(test):
|
||||
orig_get = compute_api.API.get
|
||||
orig_get_all = compute_api.API.get_all
|
||||
orig_create = compute_api.API.create
|
||||
|
||||
def fake_get(*args, **kwargs):
|
||||
return _get_instances_with_cached_ips(orig_get, *args, **kwargs)
|
||||
|
||||
def fake_get_all(*args, **kwargs):
|
||||
return _get_instances_with_cached_ips(orig_get_all, *args, **kwargs)
|
||||
|
||||
def fake_create(*args, **kwargs):
|
||||
return _create_instances_with_cached_ips(orig_create, *args, **kwargs)
|
||||
|
||||
def fake_pci_device_get_by_addr(context, node_id, dev_addr):
|
||||
return test_pci_device.fake_db_dev
|
||||
|
||||
test.stub_out('nova.db.api.pci_device_get_by_addr',
|
||||
fake_pci_device_get_by_addr)
|
||||
test.stub_out('nova.compute.api.API.get', fake_get)
|
||||
test.stub_out('nova.compute.api.API.get_all', fake_get_all)
|
||||
test.stub_out('nova.compute.api.API.create', fake_create)
|
||||
|
||||
|
||||
def _get_fake_cache():
|
||||
def _ip(ip, fixed=True, floats=None):
|
||||
ip_dict = {'address': ip, 'type': 'fixed'}
|
||||
@ -461,10 +176,9 @@ def _get_fake_cache():
|
||||
'label': 'private',
|
||||
'subnets': [{'cidr': '192.168.0.0/24',
|
||||
'ips': [_ip('192.168.0.3')]}]}}]
|
||||
if CONF.use_ipv6:
|
||||
ipv6_addr = 'fe80:b33f::a8bb:ccff:fedd:eeff'
|
||||
info[0]['network']['subnets'].append({'cidr': 'fe80:b33f::/64',
|
||||
'ips': [_ip(ipv6_addr)]})
|
||||
ipv6_addr = 'fe80:b33f::a8bb:ccff:fedd:eeff'
|
||||
info[0]['network']['subnets'].append({'cidr': 'fe80:b33f::/64',
|
||||
'ips': [_ip(ipv6_addr)]})
|
||||
return jsonutils.dumps(info)
|
||||
|
||||
|
||||
@ -509,4 +223,4 @@ def _create_instances_with_cached_ips(orig_func, *args, **kwargs):
|
||||
instance['info_cache'].network_info = fake_cache
|
||||
db.instance_info_cache_update(args[1], instance['uuid'],
|
||||
{'network_info': fake_cache})
|
||||
return (instances, reservation_id)
|
||||
return instances, reservation_id
|
||||
|
@ -1,570 +0,0 @@
|
||||
# Copyright 2012 Red Hat, Inc.
|
||||
# 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.
|
||||
|
||||
"""Tests for network API."""
|
||||
|
||||
import copy
|
||||
|
||||
import mock
|
||||
from oslo_utils.fixture import uuidsentinel as uuids
|
||||
from oslo_utils import uuidutils
|
||||
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova import network
|
||||
from nova.network import api
|
||||
from nova.network import base_api
|
||||
from nova.network import floating_ips
|
||||
from nova.network import model as network_model
|
||||
from nova import objects
|
||||
from nova.objects import fields
|
||||
from nova.objects import network_request as net_req_obj
|
||||
from nova import test
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.tests.unit.objects import test_fixed_ip
|
||||
from nova.tests.unit.objects import test_virtual_interface
|
||||
|
||||
|
||||
FAKE_UUID = 'a47ae74e-ab08-547f-9eee-ffd23fc46c16'
|
||||
|
||||
fake_info_cache = {
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'deleted': False,
|
||||
'instance_uuid': uuids.instance,
|
||||
'network_info': '[]',
|
||||
}
|
||||
|
||||
|
||||
class ApiTestCase(test.TestCase):
|
||||
def setUp(self):
|
||||
super(ApiTestCase, self).setUp()
|
||||
self.flags(use_neutron=False)
|
||||
self.network_api = network.API()
|
||||
self.context = context.RequestContext('fake-user',
|
||||
fakes.FAKE_PROJECT_ID)
|
||||
|
||||
@mock.patch('nova.objects.NetworkList.get_all')
|
||||
def test_get_all(self, mock_get_all):
|
||||
mock_get_all.return_value = mock.sentinel.get_all
|
||||
self.assertEqual(mock.sentinel.get_all,
|
||||
self.network_api.get_all(self.context))
|
||||
mock_get_all.assert_called_once_with(self.context,
|
||||
project_only=True)
|
||||
|
||||
@mock.patch('nova.objects.NetworkList.get_all')
|
||||
def test_get_all_liberal(self, mock_get_all):
|
||||
self.flags(network_manager='nova.network.manager.FlatDHCPManager')
|
||||
mock_get_all.return_value = mock.sentinel.get_all
|
||||
self.assertEqual(mock.sentinel.get_all,
|
||||
self.network_api.get_all(self.context))
|
||||
mock_get_all.assert_called_once_with(self.context,
|
||||
project_only="allow_none")
|
||||
|
||||
@mock.patch('nova.objects.NetworkList.get_all')
|
||||
def test_get_all_no_networks(self, mock_get_all):
|
||||
mock_get_all.side_effect = exception.NoNetworksFound
|
||||
self.assertEqual([], self.network_api.get_all(self.context))
|
||||
mock_get_all.assert_called_once_with(self.context,
|
||||
project_only=True)
|
||||
|
||||
@mock.patch('nova.objects.Network.get_by_uuid')
|
||||
def test_get(self, mock_get):
|
||||
mock_get.return_value = mock.sentinel.get_by_uuid
|
||||
self.assertEqual(mock.sentinel.get_by_uuid,
|
||||
self.network_api.get(self.context, uuids.instance))
|
||||
|
||||
@mock.patch('nova.objects.Network.get_by_id')
|
||||
@mock.patch('nova.db.api.virtual_interface_get_by_instance')
|
||||
def test_get_vifs_by_instance(self, mock_get_by_instance,
|
||||
mock_get_by_id):
|
||||
mock_get_by_instance.return_value = [
|
||||
dict(test_virtual_interface.fake_vif,
|
||||
network_id=123)]
|
||||
mock_get_by_id.return_value = objects.Network()
|
||||
mock_get_by_id.return_value.uuid = uuids.network_1
|
||||
instance = objects.Instance(uuid=uuids.instance)
|
||||
vifs = self.network_api.get_vifs_by_instance(self.context,
|
||||
instance)
|
||||
self.assertEqual(1, len(vifs))
|
||||
self.assertEqual(123, vifs[0].network_id)
|
||||
self.assertEqual(uuids.network_1, vifs[0].net_uuid)
|
||||
mock_get_by_instance.assert_called_once_with(
|
||||
self.context, uuids.instance)
|
||||
mock_get_by_id.assert_called_once_with(self.context, 123,
|
||||
project_only='allow_none')
|
||||
|
||||
@mock.patch('nova.objects.Network.get_by_id')
|
||||
@mock.patch('nova.db.api.virtual_interface_get_by_address')
|
||||
def test_get_vif_by_mac_address(self, mock_get_by_address,
|
||||
mock_get_by_id):
|
||||
mock_get_by_address.return_value = dict(
|
||||
test_virtual_interface.fake_vif, network_id=123)
|
||||
mock_get_by_id.return_value = objects.Network(
|
||||
uuid=uuids.network_1)
|
||||
vif = self.network_api.get_vif_by_mac_address(self.context,
|
||||
mock.sentinel.mac)
|
||||
self.assertEqual(123, vif.network_id)
|
||||
self.assertEqual(uuids.network_1, vif.net_uuid)
|
||||
mock_get_by_address.assert_called_once_with(self.context,
|
||||
mock.sentinel.mac)
|
||||
mock_get_by_id.assert_called_once_with(self.context, 123,
|
||||
project_only='allow_none')
|
||||
|
||||
def _do_test_associate_floating_ip(self, orig_instance_uuid):
|
||||
"""Test post-association logic."""
|
||||
|
||||
new_instance = objects.Instance(uuid=FAKE_UUID)
|
||||
|
||||
def fake_associate(*args, **kwargs):
|
||||
return orig_instance_uuid
|
||||
|
||||
def fake_instance_get_by_uuid(context, instance_uuid,
|
||||
columns_to_join=None,
|
||||
use_slave=None):
|
||||
if instance_uuid == orig_instance_uuid:
|
||||
self.assertIn('extra.flavor', columns_to_join)
|
||||
return fake_instance.fake_db_instance(uuid=instance_uuid)
|
||||
|
||||
def fake_get_nw_info(ctxt, instance):
|
||||
class FakeNWInfo(object):
|
||||
def json(self):
|
||||
pass
|
||||
return FakeNWInfo()
|
||||
|
||||
if orig_instance_uuid:
|
||||
expected_updated_instances = [new_instance.uuid,
|
||||
orig_instance_uuid]
|
||||
else:
|
||||
expected_updated_instances = [new_instance.uuid]
|
||||
|
||||
def fake_instance_info_cache_update(context, instance_uuid, cache):
|
||||
self.assertEqual(instance_uuid,
|
||||
expected_updated_instances.pop())
|
||||
return fake_info_cache
|
||||
|
||||
def fake_update_instance_cache_with_nw_info(api, context, instance,
|
||||
nw_info=None):
|
||||
return
|
||||
|
||||
with test.nested(
|
||||
mock.patch.object(floating_ips.FloatingIP, 'associate_floating_ip',
|
||||
fake_associate),
|
||||
mock.patch.object(self.network_api.db, 'instance_get_by_uuid',
|
||||
fake_instance_get_by_uuid),
|
||||
mock.patch.object(self.network_api, '_get_instance_nw_info',
|
||||
fake_get_nw_info),
|
||||
mock.patch.object(self.network_api.db,
|
||||
'instance_info_cache_update',
|
||||
fake_instance_info_cache_update),
|
||||
mock.patch.object(base_api, "update_instance_cache_with_nw_info",
|
||||
fake_update_instance_cache_with_nw_info)
|
||||
):
|
||||
self.network_api.associate_floating_ip(self.context,
|
||||
new_instance,
|
||||
'172.24.4.225',
|
||||
'10.0.0.2')
|
||||
|
||||
def test_associate_preassociated_floating_ip(self):
|
||||
self._do_test_associate_floating_ip(uuids.orig_uuid)
|
||||
|
||||
def test_associate_unassociated_floating_ip(self):
|
||||
self._do_test_associate_floating_ip(None)
|
||||
|
||||
def test_get_floating_ip_invalid_id(self):
|
||||
self.assertRaises(exception.InvalidID,
|
||||
self.network_api.get_floating_ip,
|
||||
self.context, '123zzz')
|
||||
|
||||
@mock.patch('nova.objects.FloatingIP.get_by_id')
|
||||
def test_get_floating_ip(self, mock_get):
|
||||
floating = mock.sentinel.floating
|
||||
mock_get.return_value = floating
|
||||
self.assertEqual(floating,
|
||||
self.network_api.get_floating_ip(self.context, 123))
|
||||
mock_get.assert_called_once_with(self.context, 123)
|
||||
|
||||
@mock.patch('nova.objects.FloatingIP.get_pool_names')
|
||||
def test_get_floating_ip_pools(self, mock_get):
|
||||
pools = ['foo', 'bar']
|
||||
mock_get.return_value = pools
|
||||
self.assertEqual(pools,
|
||||
self.network_api.get_floating_ip_pools(
|
||||
self.context))
|
||||
|
||||
@mock.patch('nova.objects.FloatingIP.get_by_address')
|
||||
def test_get_floating_ip_by_address(self, mock_get):
|
||||
floating = mock.sentinel.floating
|
||||
mock_get.return_value = floating
|
||||
self.assertEqual(floating,
|
||||
self.network_api.get_floating_ip_by_address(
|
||||
self.context, mock.sentinel.address))
|
||||
mock_get.assert_called_once_with(self.context,
|
||||
mock.sentinel.address)
|
||||
|
||||
@mock.patch('nova.objects.FloatingIPList.get_by_project')
|
||||
def test_get_floating_ips_by_project(self, mock_get):
|
||||
floatings = mock.sentinel.floating_ips
|
||||
mock_get.return_value = floatings
|
||||
self.assertEqual(floatings,
|
||||
self.network_api.get_floating_ips_by_project(
|
||||
self.context))
|
||||
mock_get.assert_called_once_with(self.context,
|
||||
self.context.project_id)
|
||||
|
||||
def _stub_migrate_instance_calls(self, method, multi_host, info):
|
||||
fake_flavor = objects.Flavor.get_by_name(self.context, 'm1.small')
|
||||
fake_flavor['rxtx_factor'] = 1.21
|
||||
fake_instance = objects.Instance(
|
||||
uuid=uuidutils.generate_uuid(dashed=False),
|
||||
project_id='fake_project_id',
|
||||
instance_type_id=fake_flavor['id'],
|
||||
flavor=fake_flavor,
|
||||
system_metadata={})
|
||||
fake_migration = objects.Migration(
|
||||
source_compute='fake_compute_source',
|
||||
dest_compute='fake_compute_dest')
|
||||
|
||||
def fake_mig_inst_method(*args, **kwargs):
|
||||
info['kwargs'] = kwargs
|
||||
|
||||
def fake_get_multi_addresses(*args, **kwargs):
|
||||
return multi_host, ['fake_float1', 'fake_float2']
|
||||
|
||||
self.stub_out('nova.network.rpcapi.NetworkAPI.' + method,
|
||||
fake_mig_inst_method)
|
||||
self.stub_out('nova.network.api.API._get_multi_addresses',
|
||||
fake_get_multi_addresses)
|
||||
|
||||
expected = {'instance_uuid': fake_instance.uuid,
|
||||
'source_compute': 'fake_compute_source',
|
||||
'dest_compute': 'fake_compute_dest',
|
||||
'rxtx_factor': 1.21,
|
||||
'project_id': 'fake_project_id',
|
||||
'floating_addresses': None}
|
||||
if multi_host:
|
||||
expected['floating_addresses'] = ['fake_float1', 'fake_float2']
|
||||
return fake_instance, fake_migration, expected
|
||||
|
||||
def test_migrate_instance_start_with_multihost(self):
|
||||
info = {'kwargs': {}}
|
||||
arg1, arg2, expected = self._stub_migrate_instance_calls(
|
||||
'migrate_instance_start', True, info)
|
||||
expected['host'] = 'fake_compute_source'
|
||||
self.network_api.migrate_instance_start(self.context, arg1, arg2)
|
||||
self.assertEqual(info['kwargs'], expected)
|
||||
|
||||
def test_migrate_instance_start_without_multihost(self):
|
||||
info = {'kwargs': {}}
|
||||
arg1, arg2, expected = self._stub_migrate_instance_calls(
|
||||
'migrate_instance_start', False, info)
|
||||
self.network_api.migrate_instance_start(self.context, arg1, arg2)
|
||||
self.assertEqual(info['kwargs'], expected)
|
||||
|
||||
def test_migrate_instance_finish_with_multihost(self):
|
||||
info = {'kwargs': {}}
|
||||
arg1, arg2, expected = self._stub_migrate_instance_calls(
|
||||
'migrate_instance_finish', True, info)
|
||||
expected['host'] = 'fake_compute_dest'
|
||||
self.network_api.migrate_instance_finish(self.context, arg1, arg2, {})
|
||||
self.assertEqual(info['kwargs'], expected)
|
||||
|
||||
def test_migrate_instance_finish_without_multihost(self):
|
||||
info = {'kwargs': {}}
|
||||
arg1, arg2, expected = self._stub_migrate_instance_calls(
|
||||
'migrate_instance_finish', False, info)
|
||||
self.network_api.migrate_instance_finish(self.context, arg1, arg2, {})
|
||||
self.assertEqual(info['kwargs'], expected)
|
||||
|
||||
def test_is_multi_host_instance_has_no_fixed_ip(self):
|
||||
with mock.patch.object(self.network_api.db, 'fixed_ip_get_by_instance',
|
||||
side_effect=exception.FixedIpNotFoundForInstance(
|
||||
instance_uuid=FAKE_UUID)):
|
||||
instance = objects.Instance(uuid=FAKE_UUID)
|
||||
result, floats = (
|
||||
self.network_api._get_multi_addresses(self.context, instance))
|
||||
self.assertFalse(result)
|
||||
|
||||
@mock.patch('nova.objects.fixed_ip.FixedIPList.get_by_instance_uuid')
|
||||
def _test_is_multi_host_network_has_no_project_id(self, is_multi_host,
|
||||
fip_get):
|
||||
network = objects.Network(
|
||||
id=123, project_id=None,
|
||||
multi_host=is_multi_host)
|
||||
fip_get.return_value = [
|
||||
objects.FixedIP(instance_uuid=FAKE_UUID, network=network,
|
||||
floating_ips=objects.FloatingIPList())]
|
||||
instance = objects.Instance(uuid=FAKE_UUID)
|
||||
result, floats = self.network_api._get_multi_addresses(self.context,
|
||||
instance)
|
||||
self.assertEqual(is_multi_host, result)
|
||||
|
||||
def test_is_multi_host_network_has_no_project_id_multi(self):
|
||||
self._test_is_multi_host_network_has_no_project_id(True)
|
||||
|
||||
def test_is_multi_host_network_has_no_project_id_non_multi(self):
|
||||
self._test_is_multi_host_network_has_no_project_id(False)
|
||||
|
||||
@mock.patch('nova.objects.fixed_ip.FixedIPList.get_by_instance_uuid')
|
||||
def _test_is_multi_host_network_has_project_id(self, is_multi_host,
|
||||
fip_get):
|
||||
network = objects.Network(
|
||||
id=123, project_id=self.context.project_id,
|
||||
multi_host=is_multi_host)
|
||||
fip_get.return_value = [
|
||||
objects.FixedIP(instance_uuid=FAKE_UUID, network=network,
|
||||
floating_ips=objects.FloatingIPList())]
|
||||
instance = objects.Instance(uuid=FAKE_UUID)
|
||||
result, floats = self.network_api._get_multi_addresses(self.context,
|
||||
instance)
|
||||
self.assertEqual(is_multi_host, result)
|
||||
|
||||
def test_is_multi_host_network_has_project_id_multi(self):
|
||||
self._test_is_multi_host_network_has_project_id(True)
|
||||
|
||||
def test_is_multi_host_network_has_project_id_non_multi(self):
|
||||
self._test_is_multi_host_network_has_project_id(False)
|
||||
|
||||
def _test_refresh_cache(self, method, *args, **kwargs):
|
||||
# This test verifies that no call to get_instance_nw_info() is made
|
||||
# from the @refresh_cache decorator for the tested method.
|
||||
with test.nested(
|
||||
mock.patch.object(self.network_api.network_rpcapi, method),
|
||||
mock.patch.object(self.network_api.network_rpcapi,
|
||||
'get_instance_nw_info'),
|
||||
mock.patch.object(network_model.NetworkInfo, 'hydrate'),
|
||||
mock.patch.object(objects.InstanceInfoCache, 'save'),
|
||||
) as (
|
||||
method_mock, nwinfo_mock, hydrate_mock, save_mock
|
||||
):
|
||||
nw_info = network_model.NetworkInfo([])
|
||||
method_mock.return_value = nw_info
|
||||
hydrate_mock.return_value = nw_info
|
||||
getattr(self.network_api, method)(*args, **kwargs)
|
||||
hydrate_mock.assert_called_once_with(nw_info)
|
||||
self.assertFalse(nwinfo_mock.called)
|
||||
|
||||
def test_allocate_for_instance_refresh_cache(self):
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
vpn = 'fake-vpn'
|
||||
requested_networks = [('fake-networks', None)]
|
||||
self._test_refresh_cache('allocate_for_instance', self.context,
|
||||
instance, vpn, requested_networks)
|
||||
|
||||
@mock.patch('nova.network.rpcapi.NetworkAPI.allocate_for_instance')
|
||||
def test_allocate_for_instance_no_nets_no_auto(self, mock_rpc_alloc):
|
||||
# Tests that nothing fails if no networks are returned and auto
|
||||
# allocation wasn't requested.
|
||||
mock_rpc_alloc.return_value = []
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
nw_info = self.network_api.allocate_for_instance(
|
||||
self.context, instance, mock.sentinel.vpn, requested_networks=None)
|
||||
self.assertEqual(0, len(nw_info))
|
||||
|
||||
@mock.patch('nova.network.rpcapi.NetworkAPI.allocate_for_instance')
|
||||
def test_allocate_for_instance_no_nets_auto_allocate(self, mock_rpc_alloc):
|
||||
# Tests that we fail when no networks are allocated and auto-allocation
|
||||
# was requested.
|
||||
|
||||
def fake_rpc_allocate(context, *args, **kwargs):
|
||||
# assert that requested_networks is nulled out
|
||||
self.assertIn('requested_networks', kwargs)
|
||||
self.assertIsNone(kwargs['requested_networks'])
|
||||
return []
|
||||
|
||||
mock_rpc_alloc.side_effect = fake_rpc_allocate
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
self.assertRaises(exception.UnableToAutoAllocateNetwork,
|
||||
self.network_api.allocate_for_instance,
|
||||
self.context, instance, mock.sentinel.vpn,
|
||||
[(net_req_obj.NETWORK_ID_AUTO, None)])
|
||||
self.assertEqual(1, mock_rpc_alloc.call_count)
|
||||
|
||||
@mock.patch('nova.network.rpcapi.NetworkAPI.deallocate_for_instance')
|
||||
def test_deallocate_for_instance_auto_allocate(self, mock_rpc_dealloc):
|
||||
# Tests that we pass requested_networks=None to the RPC API when
|
||||
# we're auto-allocating.
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
req_net = objects.NetworkRequest(
|
||||
network_id=net_req_obj.NETWORK_ID_AUTO)
|
||||
requested_networks = objects.NetworkRequestList(objects=[req_net])
|
||||
self.network_api.deallocate_for_instance(
|
||||
self.context, instance, requested_networks)
|
||||
mock_rpc_dealloc.assert_called_once_with(self.context,
|
||||
instance=instance,
|
||||
requested_networks=None)
|
||||
|
||||
def test_add_fixed_ip_to_instance_refresh_cache(self):
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
network_id = 'fake-network-id'
|
||||
self._test_refresh_cache('add_fixed_ip_to_instance', self.context,
|
||||
instance, network_id)
|
||||
|
||||
def test_remove_fixed_ip_from_instance_refresh_cache(self):
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
address = 'fake-address'
|
||||
self._test_refresh_cache('remove_fixed_ip_from_instance', self.context,
|
||||
instance, address)
|
||||
|
||||
@mock.patch('nova.db.api.fixed_ip_get_by_address')
|
||||
def test_get_fixed_ip_by_address(self, fip_get):
|
||||
fip_get.return_value = test_fixed_ip.fake_fixed_ip
|
||||
fip = self.network_api.get_fixed_ip_by_address(self.context,
|
||||
'fake-addr')
|
||||
self.assertIsInstance(fip, objects.FixedIP)
|
||||
|
||||
@mock.patch('nova.objects.FixedIP.get_by_id')
|
||||
def test_get_fixed_ip(self, mock_get_by_id):
|
||||
mock_get_by_id.return_value = mock.sentinel.fixed_ip
|
||||
self.assertEqual(mock.sentinel.fixed_ip,
|
||||
self.network_api.get_fixed_ip(self.context,
|
||||
mock.sentinel.id))
|
||||
mock_get_by_id.assert_called_once_with(self.context, mock.sentinel.id)
|
||||
|
||||
@mock.patch('nova.objects.FixedIP.get_by_floating_address')
|
||||
def test_get_instance_by_floating_address(self, mock_get_by_floating):
|
||||
mock_get_by_floating.return_value = objects.FixedIP(
|
||||
instance_uuid = uuids.instance)
|
||||
self.assertEqual(uuids.instance,
|
||||
self.network_api.get_instance_id_by_floating_address(
|
||||
self.context, mock.sentinel.floating))
|
||||
mock_get_by_floating.assert_called_once_with(self.context,
|
||||
mock.sentinel.floating)
|
||||
|
||||
@mock.patch('nova.objects.FixedIP.get_by_floating_address')
|
||||
def test_get_instance_by_floating_address_none(self, mock_get_by_floating):
|
||||
mock_get_by_floating.return_value = None
|
||||
self.assertIsNone(
|
||||
self.network_api.get_instance_id_by_floating_address(
|
||||
self.context, mock.sentinel.floating))
|
||||
mock_get_by_floating.assert_called_once_with(self.context,
|
||||
mock.sentinel.floating)
|
||||
|
||||
@mock.patch('nova.network.api.API.migrate_instance_start')
|
||||
def test_cleanup_instance_network_on_host(self, fake_migrate_start):
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
self.network_api.cleanup_instance_network_on_host(
|
||||
self.context, instance, 'fake_compute_source')
|
||||
fake_migrate_start.assert_called_once_with(
|
||||
self.context, instance,
|
||||
{'source_compute': 'fake_compute_source', 'dest_compute': None})
|
||||
|
||||
@mock.patch('nova.network.api.API.migrate_instance_finish')
|
||||
def test_setup_instance_network_on_host(self, fake_migrate_finish):
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
self.network_api.setup_instance_network_on_host(
|
||||
self.context, instance, 'fake_compute_source')
|
||||
fake_migrate_finish.assert_called_once_with(
|
||||
self.context, instance,
|
||||
{'source_compute': None, 'dest_compute': 'fake_compute_source'},
|
||||
None)
|
||||
|
||||
@mock.patch('oslo_concurrency.lockutils.lock')
|
||||
@mock.patch.object(api.API, '_get_instance_nw_info')
|
||||
@mock.patch('nova.network.base_api.update_instance_cache_with_nw_info')
|
||||
def test_get_instance_nw_info(self, mock_update, mock_get, mock_lock):
|
||||
fake_result = mock.sentinel.get_nw_info_result
|
||||
mock_get.return_value = fake_result
|
||||
instance = fake_instance.fake_instance_obj(self.context)
|
||||
result = self.network_api.get_instance_nw_info(self.context, instance)
|
||||
mock_get.assert_called_once_with(self.context, instance)
|
||||
mock_update.assert_called_once_with(self.network_api, self.context,
|
||||
instance, nw_info=fake_result)
|
||||
self.assertEqual(fake_result, result)
|
||||
|
||||
|
||||
@mock.patch('nova.network.api.API')
|
||||
@mock.patch('nova.db.api.instance_info_cache_update')
|
||||
class TestUpdateInstanceCache(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(TestUpdateInstanceCache, self).setUp()
|
||||
self.context = context.get_admin_context()
|
||||
self.instance = objects.Instance(uuid=FAKE_UUID, deleted=False)
|
||||
vifs = [network_model.VIF(id='super_vif')]
|
||||
self.nw_info = network_model.NetworkInfo(vifs)
|
||||
self.nw_json = fields.NetworkModel.to_primitive(self, 'network_info',
|
||||
self.nw_info)
|
||||
|
||||
def test_update_nw_info_none(self, db_mock, api_mock):
|
||||
api_mock._get_instance_nw_info.return_value = self.nw_info
|
||||
info_cache = copy.deepcopy(fake_info_cache)
|
||||
info_cache.update({'network_info': self.nw_json})
|
||||
db_mock.return_value = info_cache
|
||||
base_api.update_instance_cache_with_nw_info(api_mock, self.context,
|
||||
self.instance, None)
|
||||
api_mock._get_instance_nw_info.assert_called_once_with(self.context,
|
||||
self.instance)
|
||||
db_mock.assert_called_once_with(self.context, self.instance.uuid,
|
||||
{'network_info': self.nw_json})
|
||||
self.assertEqual(self.nw_info, self.instance.info_cache.network_info)
|
||||
|
||||
def test_update_nw_info_none_instance_deleted(self, db_mock, api_mock):
|
||||
instance = objects.Instance(uuid=FAKE_UUID, deleted=True)
|
||||
base_api.update_instance_cache_with_nw_info(
|
||||
api_mock, self.context, instance)
|
||||
self.assertFalse(api_mock.called)
|
||||
|
||||
def test_update_nw_info_one_network(self, db_mock, api_mock):
|
||||
info_cache = copy.deepcopy(fake_info_cache)
|
||||
info_cache.update({'network_info': self.nw_json})
|
||||
db_mock.return_value = info_cache
|
||||
base_api.update_instance_cache_with_nw_info(api_mock, self.context,
|
||||
self.instance, self.nw_info)
|
||||
self.assertFalse(api_mock._get_instance_nw_info.called)
|
||||
db_mock.assert_called_once_with(self.context, self.instance.uuid,
|
||||
{'network_info': self.nw_json})
|
||||
self.assertEqual(self.nw_info, self.instance.info_cache.network_info)
|
||||
|
||||
def test_update_nw_info_empty_list(self, db_mock, api_mock):
|
||||
new_nw_info = network_model.NetworkInfo([])
|
||||
db_mock.return_value = fake_info_cache
|
||||
base_api.update_instance_cache_with_nw_info(api_mock, self.context,
|
||||
self.instance, new_nw_info)
|
||||
self.assertFalse(api_mock._get_instance_nw_info.called)
|
||||
db_mock.assert_called_once_with(self.context, self.instance.uuid,
|
||||
{'network_info': '[]'})
|
||||
self.assertEqual(new_nw_info, self.instance.info_cache.network_info)
|
||||
|
||||
def test_decorator_return_object(self, db_mock, api_mock):
|
||||
db_mock.return_value = fake_info_cache
|
||||
|
||||
@base_api.refresh_cache
|
||||
def func(self, context, instance):
|
||||
return network_model.NetworkInfo([])
|
||||
func(api_mock, self.context, self.instance)
|
||||
self.assertFalse(api_mock._get_instance_nw_info.called)
|
||||
db_mock.assert_called_once_with(self.context, self.instance.uuid,
|
||||
{'network_info': '[]'})
|
||||
|
||||
def test_decorator_return_none(self, db_mock, api_mock):
|
||||
db_mock.return_value = fake_info_cache
|
||||
|
||||
@base_api.refresh_cache
|
||||
def func(self, context, instance):
|
||||
pass
|
||||
api_mock._get_instance_nw_info.return_value = self.nw_info
|
||||
func(api_mock, self.context, self.instance)
|
||||
api_mock._get_instance_nw_info.assert_called_once_with(self.context,
|
||||
self.instance)
|
||||
db_mock.assert_called_once_with(self.context, self.instance.uuid,
|
||||
{'network_info': self.nw_json})
|
||||
|
||||
|
||||
class NetworkHooksTestCase(test.BaseHookTestCase):
|
||||
def test_instance_network_info_hook(self):
|
||||
info_func = base_api.update_instance_cache_with_nw_info
|
||||
self.assert_has_hook('instance_network_info', info_func)
|
@ -1,26 +0,0 @@
|
||||
# Copyright 2015 OpenStack Foundation
|
||||
# All Rights Reserved.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from nova.network import l3
|
||||
from nova import test
|
||||
|
||||
|
||||
class L3DriverTestCase(test.NoDBTestCase):
|
||||
|
||||
def test_linuxnetl3_driver_signatures(self):
|
||||
self.assertPublicAPISignatures(l3.L3Driver, l3.LinuxNetL3)
|
||||
|
||||
def test_nulll3_driver_signatures(self):
|
||||
self.assertPublicAPISignatures(l3.L3Driver, l3.NullL3)
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -1,417 +0,0 @@
|
||||
# Copyright 2013 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.
|
||||
|
||||
"""
|
||||
Unit Tests for nova.network.rpcapi
|
||||
"""
|
||||
|
||||
import collections
|
||||
|
||||
import mock
|
||||
from oslo_config import cfg
|
||||
|
||||
from nova import context
|
||||
from nova import exception
|
||||
from nova.network import rpcapi as network_rpcapi
|
||||
from nova.objects import base as objects_base
|
||||
from nova import test
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.tests.unit import fake_network
|
||||
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class NetworkRpcAPITestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(NetworkRpcAPITestCase, self).setUp()
|
||||
self.flags(multi_host=True)
|
||||
|
||||
# Used to specify the default value expected if no real value is passed
|
||||
DefaultArg = collections.namedtuple('DefaultArg', ['value'])
|
||||
|
||||
def _test_network_api(self, method, rpc_method, **kwargs):
|
||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
|
||||
rpcapi = network_rpcapi.NetworkAPI()
|
||||
self.assertIsNotNone(rpcapi.client)
|
||||
self.assertEqual(network_rpcapi.RPC_TOPIC,
|
||||
rpcapi.client.target.topic)
|
||||
|
||||
expected_retval = 'foo' if rpc_method == 'call' else None
|
||||
expected_version = kwargs.pop('version', None)
|
||||
expected_fanout = kwargs.pop('fanout', None)
|
||||
expected_kwargs = kwargs.copy()
|
||||
|
||||
for k, v in expected_kwargs.items():
|
||||
if isinstance(v, self.DefaultArg):
|
||||
expected_kwargs[k] = v.value
|
||||
kwargs.pop(k)
|
||||
|
||||
prepare_kwargs = {}
|
||||
if expected_version:
|
||||
prepare_kwargs['version'] = expected_version
|
||||
if expected_fanout:
|
||||
prepare_kwargs['fanout'] = True
|
||||
|
||||
if 'source_compute' in expected_kwargs:
|
||||
# Fix up for migrate_instance_* calls.
|
||||
expected_kwargs['source'] = expected_kwargs.pop('source_compute')
|
||||
expected_kwargs['dest'] = expected_kwargs.pop('dest_compute')
|
||||
|
||||
targeted_methods = [
|
||||
'lease_fixed_ip', 'release_fixed_ip', 'rpc_setup_network_on_host',
|
||||
'_rpc_allocate_fixed_ip', 'deallocate_fixed_ip', 'update_dns',
|
||||
'_associate_floating_ip', '_disassociate_floating_ip',
|
||||
'lease_fixed_ip', 'release_fixed_ip', 'migrate_instance_start',
|
||||
'migrate_instance_finish',
|
||||
'allocate_for_instance', 'deallocate_for_instance',
|
||||
]
|
||||
targeted_by_instance = ['deallocate_for_instance']
|
||||
if method in targeted_methods and ('host' in expected_kwargs or
|
||||
'instance' in expected_kwargs):
|
||||
if method in targeted_by_instance:
|
||||
host = expected_kwargs['instance']['host']
|
||||
else:
|
||||
host = expected_kwargs['host']
|
||||
if method not in ['allocate_for_instance',
|
||||
'deallocate_fixed_ip']:
|
||||
expected_kwargs.pop('host')
|
||||
if CONF.multi_host:
|
||||
prepare_kwargs['server'] = host
|
||||
|
||||
with test.nested(
|
||||
mock.patch.object(rpcapi.client, rpc_method),
|
||||
mock.patch.object(rpcapi.client, 'prepare'),
|
||||
mock.patch.object(rpcapi.client, 'can_send_version'),
|
||||
) as (
|
||||
rpc_mock, prepare_mock, csv_mock
|
||||
):
|
||||
|
||||
version_check = [
|
||||
'deallocate_for_instance', 'deallocate_fixed_ip',
|
||||
'allocate_for_instance', 'release_fixed_ip',
|
||||
'set_network_host', 'setup_networks_on_host'
|
||||
]
|
||||
if method in version_check:
|
||||
csv_mock.return_value = True
|
||||
|
||||
if prepare_kwargs:
|
||||
prepare_mock.return_value = rpcapi.client
|
||||
|
||||
if rpc_method == 'call':
|
||||
rpc_mock.return_value = 'foo'
|
||||
else:
|
||||
rpc_mock.return_value = None
|
||||
|
||||
retval = getattr(rpcapi, method)(ctxt, **kwargs)
|
||||
self.assertEqual(expected_retval, retval)
|
||||
|
||||
if method in version_check:
|
||||
csv_mock.assert_called_once_with(mock.ANY)
|
||||
if prepare_kwargs:
|
||||
prepare_mock.assert_called_once_with(**prepare_kwargs)
|
||||
rpc_mock.assert_called_once_with(ctxt, method, **expected_kwargs)
|
||||
|
||||
def test_create_networks(self):
|
||||
self._test_network_api('create_networks', rpc_method='call',
|
||||
arg1='arg', arg2='arg')
|
||||
|
||||
def test_delete_network(self):
|
||||
self._test_network_api('delete_network', rpc_method='call',
|
||||
uuid='fake_uuid', fixed_range='range')
|
||||
|
||||
def test_allocate_for_instance(self):
|
||||
self._test_network_api('allocate_for_instance', rpc_method='call',
|
||||
instance_id='fake_id', project_id='fake_id', host='fake_host',
|
||||
rxtx_factor='fake_factor', vpn=False, requested_networks={},
|
||||
macs=[], version='1.13')
|
||||
|
||||
def test_deallocate_for_instance(self):
|
||||
instance = fake_instance.fake_instance_obj(context.get_admin_context())
|
||||
self._test_network_api('deallocate_for_instance', rpc_method='call',
|
||||
requested_networks=self.DefaultArg(None), instance=instance,
|
||||
version='1.11')
|
||||
|
||||
def test_deallocate_for_instance_with_expected_networks(self):
|
||||
instance = fake_instance.fake_instance_obj(context.get_admin_context())
|
||||
self._test_network_api('deallocate_for_instance', rpc_method='call',
|
||||
instance=instance, requested_networks={}, version='1.11')
|
||||
|
||||
def test_release_dhcp(self):
|
||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
|
||||
dev = 'eth0'
|
||||
address = '192.168.65.158'
|
||||
vif_address = '00:0c:29:2c:b2:64'
|
||||
host = 'fake-host'
|
||||
|
||||
rpcapi = network_rpcapi.NetworkAPI()
|
||||
call_mock = mock.Mock()
|
||||
cctxt_mock = mock.Mock(call=call_mock)
|
||||
|
||||
with test.nested(
|
||||
mock.patch.object(rpcapi.client, 'can_send_version',
|
||||
return_value=True),
|
||||
mock.patch.object(rpcapi.client, 'prepare',
|
||||
return_value=cctxt_mock)
|
||||
) as (
|
||||
can_send_mock, prepare_mock
|
||||
):
|
||||
rpcapi.release_dhcp(ctxt, host, dev, address, vif_address)
|
||||
|
||||
can_send_mock.assert_called_once_with('1.17')
|
||||
prepare_mock.assert_called_once_with(server=host, version='1.17')
|
||||
call_mock.assert_called_once_with(ctxt, 'release_dhcp', dev=dev,
|
||||
address=address,
|
||||
vif_address=vif_address)
|
||||
|
||||
def test_release_dhcp_v116(self):
|
||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
|
||||
dev = 'eth0'
|
||||
address = '192.168.65.158'
|
||||
vif_address = '00:0c:29:2c:b2:64'
|
||||
host = 'fake-host'
|
||||
rpcapi = network_rpcapi.NetworkAPI()
|
||||
|
||||
with mock.patch.object(rpcapi.client, 'can_send_version',
|
||||
return_value=False) as can_send_mock:
|
||||
self.assertRaises(exception.RPCPinnedToOldVersion,
|
||||
rpcapi.release_dhcp, ctxt, host, dev, address,
|
||||
vif_address)
|
||||
can_send_mock.assert_called_once_with('1.17')
|
||||
|
||||
def test_add_fixed_ip_to_instance(self):
|
||||
self._test_network_api('add_fixed_ip_to_instance', rpc_method='call',
|
||||
instance_id='fake_id', rxtx_factor='fake_factor',
|
||||
host='fake_host', network_id='fake_id', version='1.9')
|
||||
|
||||
def test_remove_fixed_ip_from_instance(self):
|
||||
self._test_network_api('remove_fixed_ip_from_instance',
|
||||
rpc_method='call', instance_id='fake_id',
|
||||
rxtx_factor='fake_factor', host='fake_host',
|
||||
address='fake_address', version='1.9')
|
||||
|
||||
def test_get_instance_nw_info(self):
|
||||
self._test_network_api('get_instance_nw_info', rpc_method='call',
|
||||
instance_id='fake_id', rxtx_factor='fake_factor',
|
||||
host='fake_host', project_id='fake_id', version='1.9')
|
||||
|
||||
def test_validate_networks(self):
|
||||
self._test_network_api('validate_networks', rpc_method='call',
|
||||
networks={})
|
||||
|
||||
def test_get_dns_domains(self):
|
||||
self._test_network_api('get_dns_domains', rpc_method='call')
|
||||
|
||||
def test_add_dns_entry(self):
|
||||
self._test_network_api('add_dns_entry', rpc_method='call',
|
||||
address='addr', name='name', dns_type='foo', domain='domain')
|
||||
|
||||
def test_modify_dns_entry(self):
|
||||
self._test_network_api('modify_dns_entry', rpc_method='call',
|
||||
address='addr', name='name', domain='domain')
|
||||
|
||||
def test_delete_dns_entry(self):
|
||||
self._test_network_api('delete_dns_entry', rpc_method='call',
|
||||
name='name', domain='domain')
|
||||
|
||||
def test_delete_dns_domain(self):
|
||||
self._test_network_api('delete_dns_domain', rpc_method='call',
|
||||
domain='fake_domain')
|
||||
|
||||
def test_get_dns_entries_by_address(self):
|
||||
self._test_network_api('get_dns_entries_by_address', rpc_method='call',
|
||||
address='fake_address', domain='fake_domain')
|
||||
|
||||
def test_get_dns_entries_by_name(self):
|
||||
self._test_network_api('get_dns_entries_by_name', rpc_method='call',
|
||||
name='fake_name', domain='fake_domain')
|
||||
|
||||
def test_create_private_dns_domain(self):
|
||||
self._test_network_api('create_private_dns_domain', rpc_method='call',
|
||||
domain='fake_domain', av_zone='fake_zone')
|
||||
|
||||
def test_create_public_dns_domain(self):
|
||||
self._test_network_api('create_public_dns_domain', rpc_method='call',
|
||||
domain='fake_domain', project='fake_project')
|
||||
|
||||
def test_setup_networks_on_host(self):
|
||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
instance = fake_instance.fake_instance_obj(ctxt)
|
||||
self._test_network_api('setup_networks_on_host', rpc_method='call',
|
||||
instance_id=instance.id, host='fake_host', teardown=False,
|
||||
instance=instance, version='1.16')
|
||||
|
||||
def test_setup_networks_on_host_v1_0(self):
|
||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
instance = fake_instance.fake_instance_obj(ctxt)
|
||||
host = 'fake_host'
|
||||
teardown = True
|
||||
rpcapi = network_rpcapi.NetworkAPI()
|
||||
call_mock = mock.Mock()
|
||||
cctxt_mock = mock.Mock(call=call_mock)
|
||||
with test.nested(
|
||||
mock.patch.object(rpcapi.client, 'can_send_version',
|
||||
return_value=False),
|
||||
mock.patch.object(rpcapi.client, 'prepare',
|
||||
return_value=cctxt_mock)
|
||||
) as (
|
||||
can_send_mock, prepare_mock
|
||||
):
|
||||
rpcapi.setup_networks_on_host(ctxt, instance.id, host, teardown,
|
||||
instance)
|
||||
# assert our mocks were called as expected
|
||||
can_send_mock.assert_called_once_with('1.16')
|
||||
prepare_mock.assert_called_once_with(version='1.0')
|
||||
call_mock.assert_called_once_with(ctxt, 'setup_networks_on_host',
|
||||
host=host, teardown=teardown,
|
||||
instance_id=instance.id)
|
||||
|
||||
def test_lease_fixed_ip(self):
|
||||
self._test_network_api('lease_fixed_ip', rpc_method='cast',
|
||||
host='fake_host', address='fake_addr')
|
||||
|
||||
def test_release_fixed_ip(self):
|
||||
self._test_network_api('release_fixed_ip', rpc_method='cast',
|
||||
host='fake_host', address='fake_addr', mac='fake_mac',
|
||||
version='1.14')
|
||||
|
||||
def test_release_fixed_ip_no_mac_support(self):
|
||||
# Tests that the mac kwarg is not passed when we can't send version
|
||||
# 1.14 to the network manager.
|
||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
address = '192.168.65.158'
|
||||
host = 'fake-host'
|
||||
mac = '00:0c:29:2c:b2:64'
|
||||
rpcapi = network_rpcapi.NetworkAPI()
|
||||
cast_mock = mock.Mock()
|
||||
cctxt_mock = mock.Mock(cast=cast_mock)
|
||||
with test.nested(
|
||||
mock.patch.object(rpcapi.client, 'can_send_version',
|
||||
return_value=False),
|
||||
mock.patch.object(rpcapi.client, 'prepare',
|
||||
return_value=cctxt_mock)
|
||||
) as (
|
||||
can_send_mock, prepare_mock
|
||||
):
|
||||
rpcapi.release_fixed_ip(ctxt, address, host, mac)
|
||||
# assert our mocks were called as expected 232
|
||||
can_send_mock.assert_called_once_with('1.14')
|
||||
prepare_mock.assert_called_once_with(server=host, version='1.0')
|
||||
cast_mock.assert_called_once_with(ctxt, 'release_fixed_ip',
|
||||
address=address)
|
||||
|
||||
def test_set_network_host(self):
|
||||
network = fake_network.fake_network_obj(context.get_admin_context())
|
||||
self._test_network_api('set_network_host', rpc_method='call',
|
||||
network_ref=network, version='1.15')
|
||||
|
||||
def test_set_network_host_network_object_to_primitive(self):
|
||||
# Tests that the network object is converted to a primitive if it
|
||||
# can't send version 1.15.
|
||||
ctxt = context.RequestContext('fake_user', 'fake_project')
|
||||
network = fake_network.fake_network_obj(ctxt)
|
||||
network_dict = objects_base.obj_to_primitive(network)
|
||||
rpcapi = network_rpcapi.NetworkAPI()
|
||||
call_mock = mock.Mock()
|
||||
cctxt_mock = mock.Mock(call=call_mock)
|
||||
with test.nested(
|
||||
mock.patch.object(rpcapi.client, 'can_send_version',
|
||||
return_value=False),
|
||||
mock.patch.object(rpcapi.client, 'prepare',
|
||||
return_value=cctxt_mock)
|
||||
) as (
|
||||
can_send_mock, prepare_mock
|
||||
):
|
||||
rpcapi.set_network_host(ctxt, network)
|
||||
# assert our mocks were called as expected
|
||||
can_send_mock.assert_called_once_with('1.15')
|
||||
prepare_mock.assert_called_once_with(version='1.0')
|
||||
call_mock.assert_called_once_with(ctxt, 'set_network_host',
|
||||
network_ref=network_dict)
|
||||
|
||||
def test_rpc_setup_network_on_host(self):
|
||||
self._test_network_api('rpc_setup_network_on_host', rpc_method='call',
|
||||
network_id='fake_id', teardown=False, host='fake_host')
|
||||
|
||||
def test_rpc_allocate_fixed_ip(self):
|
||||
self._test_network_api('_rpc_allocate_fixed_ip', rpc_method='call',
|
||||
instance_id='fake_id', network_id='fake_id', address='addr',
|
||||
vpn=True, host='fake_host')
|
||||
|
||||
def test_deallocate_fixed_ip(self):
|
||||
instance = fake_instance.fake_db_instance()
|
||||
self._test_network_api('deallocate_fixed_ip', rpc_method='call',
|
||||
address='fake_addr', host='fake_host', instance=instance,
|
||||
version='1.12')
|
||||
|
||||
def test_update_dns(self):
|
||||
self._test_network_api('update_dns', rpc_method='cast', fanout=True,
|
||||
network_ids='fake_id', version='1.3')
|
||||
|
||||
def test__associate_floating_ip(self):
|
||||
self._test_network_api('_associate_floating_ip', rpc_method='call',
|
||||
floating_address='fake_addr', fixed_address='fixed_address',
|
||||
interface='fake_interface', host='fake_host',
|
||||
instance_uuid='fake_uuid', version='1.6')
|
||||
|
||||
def test__disassociate_floating_ip(self):
|
||||
self._test_network_api('_disassociate_floating_ip', rpc_method='call',
|
||||
address='fake_addr', interface='fake_interface',
|
||||
host='fake_host', instance_uuid='fake_uuid', version='1.6')
|
||||
|
||||
def test_migrate_instance_start(self):
|
||||
self._test_network_api('migrate_instance_start', rpc_method='call',
|
||||
instance_uuid='fake_instance_uuid',
|
||||
rxtx_factor='fake_factor',
|
||||
project_id='fake_project',
|
||||
source_compute='fake_src_compute',
|
||||
dest_compute='fake_dest_compute',
|
||||
floating_addresses='fake_floating_addresses',
|
||||
host=self.DefaultArg(None),
|
||||
version='1.2')
|
||||
|
||||
def test_migrate_instance_start_multi_host(self):
|
||||
self._test_network_api('migrate_instance_start', rpc_method='call',
|
||||
instance_uuid='fake_instance_uuid',
|
||||
rxtx_factor='fake_factor',
|
||||
project_id='fake_project',
|
||||
source_compute='fake_src_compute',
|
||||
dest_compute='fake_dest_compute',
|
||||
floating_addresses='fake_floating_addresses',
|
||||
host='fake_host',
|
||||
version='1.2')
|
||||
|
||||
def test_migrate_instance_finish(self):
|
||||
self._test_network_api('migrate_instance_finish', rpc_method='call',
|
||||
instance_uuid='fake_instance_uuid',
|
||||
rxtx_factor='fake_factor',
|
||||
project_id='fake_project',
|
||||
source_compute='fake_src_compute',
|
||||
dest_compute='fake_dest_compute',
|
||||
floating_addresses='fake_floating_addresses',
|
||||
host=self.DefaultArg(None),
|
||||
version='1.2')
|
||||
|
||||
def test_migrate_instance_finish_multi_host(self):
|
||||
self._test_network_api('migrate_instance_finish', rpc_method='call',
|
||||
instance_uuid='fake_instance_uuid',
|
||||
rxtx_factor='fake_factor',
|
||||
project_id='fake_project',
|
||||
source_compute='fake_src_compute',
|
||||
dest_compute='fake_dest_compute',
|
||||
floating_addresses='fake_floating_addresses',
|
||||
host='fake_host',
|
||||
version='1.2')
|
@ -1,85 +0,0 @@
|
||||
# Copyright (C) 2014, 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 mock
|
||||
|
||||
from nova.db import api as db
|
||||
from nova.objects import dns_domain
|
||||
from nova.tests.unit.objects import test_objects
|
||||
|
||||
|
||||
fake_dnsd = {
|
||||
'created_at': None,
|
||||
'updated_at': None,
|
||||
'deleted_at': None,
|
||||
'deleted': 0,
|
||||
'domain': 'blah.example.com',
|
||||
'scope': 'private',
|
||||
'availability_zone': 'overthere',
|
||||
'project_id': '867530niner',
|
||||
}
|
||||
|
||||
|
||||
class _TestDNSDomain(object):
|
||||
@staticmethod
|
||||
def _compare(test, db, obj):
|
||||
for field, value in db.items():
|
||||
test.assertEqual(db[field], getattr(obj, field))
|
||||
|
||||
def test_get_by_domain(self):
|
||||
with mock.patch.object(db, 'dnsdomain_get') as get:
|
||||
get.return_value = fake_dnsd
|
||||
dnsd = dns_domain.DNSDomain.get_by_domain(self.context, 'domain')
|
||||
self._compare(self, fake_dnsd, dnsd)
|
||||
|
||||
def test_register_for_zone(self):
|
||||
dns_domain.DNSDomain.register_for_zone(self.context.elevated(),
|
||||
'domain', 'zone')
|
||||
dnsd = dns_domain.DNSDomain.get_by_domain(self.context, 'domain')
|
||||
self.assertEqual('domain', dnsd.domain)
|
||||
self.assertEqual('zone', dnsd.availability_zone)
|
||||
|
||||
def test_register_for_project(self):
|
||||
dns_domain.DNSDomain.register_for_project(self.context.elevated(),
|
||||
'domain', 'project')
|
||||
dnsd = dns_domain.DNSDomain.get_by_domain(self.context, 'domain')
|
||||
self.assertEqual('domain', dnsd.domain)
|
||||
self.assertEqual('project', dnsd.project_id)
|
||||
|
||||
def test_delete_by_domain(self):
|
||||
dns_domain.DNSDomain.register_for_zone(self.context.elevated(),
|
||||
'domain', 'zone')
|
||||
dnsd = dns_domain.DNSDomain.get_by_domain(self.context, 'domain')
|
||||
self.assertEqual('domain', dnsd.domain)
|
||||
self.assertEqual('zone', dnsd.availability_zone)
|
||||
|
||||
dns_domain.DNSDomain.delete_by_domain(self.context.elevated(),
|
||||
'domain')
|
||||
dnsd = dns_domain.DNSDomain.get_by_domain(self.context, 'domain')
|
||||
self.assertIsNone(dnsd)
|
||||
|
||||
def test_get_all(self):
|
||||
with mock.patch.object(db, 'dnsdomain_get_all') as get:
|
||||
get.return_value = [fake_dnsd]
|
||||
dns_domain.DNSDomainList.get_all(self.context)
|
||||
|
||||
|
||||
class TestDNSDomainObject(test_objects._LocalTest,
|
||||
_TestDNSDomain):
|
||||
pass
|
||||
|
||||
|
||||
class TestRemoteDNSDomainObject(test_objects._RemoteTest,
|
||||
_TestDNSDomain):
|
||||
pass
|
@ -1047,8 +1047,6 @@ object_data = {
|
||||
'ComputeNodeList': '1.17-52f3b0962b1c86b98590144463ebb192',
|
||||
'ConsoleAuthToken': '1.1-8da320fb065080eb4d3c2e5c59f8bf52',
|
||||
'CpuDiagnostics': '1.0-d256f2e442d1b837735fd17dfe8e3d47',
|
||||
'DNSDomain': '1.0-7b0b2dab778454b6a7b6c66afe163a1a',
|
||||
'DNSDomainList': '1.0-4ee0d9efdfd681fed822da88376e04d2',
|
||||
'Destination': '1.4-3b440d29459e2c98987ad5b25ad1cb2c',
|
||||
'DeviceBus': '1.0-77509ea1ea0dd750d5864b9bd87d3f9d',
|
||||
'DeviceMetadata': '1.0-04eb8fd218a49cbc3b1e54b774d179f7',
|
||||
|
@ -16,9 +16,7 @@
|
||||
import mock
|
||||
|
||||
from oslo_concurrency import processutils
|
||||
import six
|
||||
|
||||
from nova import exception
|
||||
import nova.privsep.linux_net
|
||||
from nova import test
|
||||
from nova.tests import fixtures
|
||||
@ -32,11 +30,6 @@ class LinuxNetTestCase(test.NoDBTestCase):
|
||||
super(LinuxNetTestCase, self).setUp()
|
||||
self.useFixture(fixtures.PrivsepFixture())
|
||||
|
||||
def test_bridge_add_interface(self, mock_execute):
|
||||
nova.privsep.linux_net.bridge_add_interface('br0', 'eth0')
|
||||
cmd = ['brctl', 'addif', 'br0', 'eth0']
|
||||
mock_execute.assert_called_once_with(*cmd, check_exit_code=False)
|
||||
|
||||
@mock.patch('os.path.exists')
|
||||
def test_device_exists(self, mock_exists, mock_execute):
|
||||
nova.privsep.linux_net.device_exists('eth0')
|
||||
@ -123,115 +116,8 @@ class LinuxNetTestCase(test.NoDBTestCase):
|
||||
nova.privsep.linux_net.create_tap_dev,
|
||||
'tap42', multiqueue=True)
|
||||
|
||||
@mock.patch('nova.privsep.linux_net.ipv4_forwarding_check',
|
||||
return_value=False)
|
||||
def test_enable_ipv4_forwarding_required(self, mock_check, mock_execute):
|
||||
nova.privsep.linux_net.enable_ipv4_forwarding()
|
||||
mock_check.assert_called_once()
|
||||
mock_execute.assert_called_once_with(
|
||||
'sysctl', '-w', 'net.ipv4.ip_forward=1')
|
||||
|
||||
@mock.patch('nova.privsep.linux_net.ipv4_forwarding_check',
|
||||
return_value=True)
|
||||
def test_enable_ipv4_forwarding_redundant(self, mock_check, mock_execute):
|
||||
nova.privsep.linux_net.enable_ipv4_forwarding()
|
||||
mock_check.assert_called_once()
|
||||
mock_execute.assert_not_called()
|
||||
|
||||
def test_modify_ebtables_insert_rule(self, mock_execute):
|
||||
table = 'filter'
|
||||
rule = 'INPUT -p ARP -i %s --arp-ip-dst %s -j DROP'.split()
|
||||
|
||||
nova.privsep.linux_net.modify_ebtables(table, rule, insert_rule=True)
|
||||
|
||||
cmd = ['ebtables', '--concurrent', '-t', table] + ['-I'] + rule
|
||||
mock_execute.assert_called_once_with(*cmd, check_exit_code=[0])
|
||||
|
||||
def test_modify_ebtables_remove_rule(self, mock_execute):
|
||||
table = 'filter'
|
||||
rule = 'INPUT -p ARP -i %s --arp-ip-dst %s -j DROP'.split()
|
||||
|
||||
nova.privsep.linux_net.modify_ebtables(table, rule, insert_rule=False)
|
||||
|
||||
cmd = ['ebtables', '--concurrent', '-t', table] + ['-D'] + rule
|
||||
mock_execute.assert_called_once_with(*cmd, check_exit_code=[0])
|
||||
|
||||
def test_add_vlan(self, mock_execute):
|
||||
nova.privsep.linux_net.add_vlan('eth0', 'vlan_name', 1)
|
||||
cmd = ['ip', 'link', 'add', 'link', 'eth0', 'name', 'vlan_name',
|
||||
'type', 'vlan', 'id', 1]
|
||||
mock_execute.assert_called_once_with(*cmd, check_exit_code=[0, 2, 254])
|
||||
|
||||
def test_iptables_get_rules(self, mock_execute):
|
||||
nova.privsep.linux_net.iptables_get_rules()
|
||||
cmd = ['iptables-save', '-c']
|
||||
mock_execute.assert_called_once_with(*cmd, attempts=5)
|
||||
|
||||
def test_iptables_get_rules_ipv6(self, mock_execute):
|
||||
nova.privsep.linux_net.iptables_get_rules(ipv4=False)
|
||||
cmd = ['ip6tables-save', '-c']
|
||||
mock_execute.assert_called_once_with(*cmd, attempts=5)
|
||||
|
||||
def test_iptables_set_rules(self, mock_execute):
|
||||
rules = [
|
||||
"# Generated by iptables-save v1.8.2 on Mon Aug 19 11:25:48 2019",
|
||||
"*security",
|
||||
":INPUT ACCEPT [508089:729290563]",
|
||||
":FORWARD ACCEPT [247333:239588306]",
|
||||
":OUTPUT ACCEPT [340769:25538424]",
|
||||
":FORWARD_direct - [0:0]",
|
||||
":INPUT_direct - [0:0]",
|
||||
":OUTPUT_direct - [0:0]",
|
||||
"-A INPUT -j INPUT_direct",
|
||||
"-A FORWARD -j FORWARD_direct",
|
||||
"-A OUTPUT -j OUTPUT_direct",
|
||||
"COMMIT",
|
||||
"# Completed on Mon Aug 19 11:25:48 2019",
|
||||
]
|
||||
rules_str = six.b('\n'.join(rules))
|
||||
|
||||
nova.privsep.linux_net.iptables_set_rules(rules)
|
||||
cmd = ['iptables-restore', '-c']
|
||||
mock_execute.assert_called_once_with(*cmd, process_input=rules_str,
|
||||
attempts=5)
|
||||
|
||||
def test_iptables_set_rules_ipv6(self, mock_execute):
|
||||
rules = [
|
||||
"# Generated by ip6tables-save v1.8.2 on Mon Aug 19 12:00:29 2019",
|
||||
"*security",
|
||||
":INPUT ACCEPT [56:10115]",
|
||||
":FORWARD ACCEPT [0:0]",
|
||||
":OUTPUT ACCEPT [147:15301]",
|
||||
":FORWARD_direct - [0:0]",
|
||||
":INPUT_direct - [0:0]",
|
||||
":OUTPUT_direct - [0:0]",
|
||||
"-A INPUT -j INPUT_direct",
|
||||
"-A FORWARD -j FORWARD_direct",
|
||||
"-A OUTPUT -j OUTPUT_direct",
|
||||
"COMMIT",
|
||||
"# Completed on Mon Aug 19 12:00:29 2019",
|
||||
]
|
||||
rules_str = six.b('\n'.join(rules))
|
||||
|
||||
nova.privsep.linux_net.iptables_set_rules(rules, ipv4=False)
|
||||
cmd = ['ip6tables-restore', '-c']
|
||||
mock_execute.assert_called_once_with(*cmd, process_input=rules_str,
|
||||
attempts=5)
|
||||
|
||||
def test_ovs_plug__fail(self, mock_execute):
|
||||
mock_execute.side_effect = processutils.ProcessExecutionError
|
||||
|
||||
exc = self.assertRaises(exception.OVSConfigurationFailure,
|
||||
nova.privsep.linux_net.ovs_plug,
|
||||
60, 'int-br', 'eth0', '00:14:22:01:23:45')
|
||||
self.assertIsInstance(exc.kwargs['inner_exception'],
|
||||
processutils.ProcessExecutionError)
|
||||
|
||||
def test_ovs_unplug__fail(self, mock_execute):
|
||||
mock_execute.side_effect = processutils.ProcessExecutionError
|
||||
|
||||
exc = self.assertRaises(exception.OVSConfigurationFailure,
|
||||
nova.privsep.linux_net.ovs_unplug,
|
||||
60, 'int-br', 'eth0')
|
||||
self.assertIsInstance(exc.kwargs['inner_exception'],
|
||||
processutils.ProcessExecutionError)
|
||||
|
@ -18,7 +18,6 @@ import os
|
||||
|
||||
import nova.privsep.utils
|
||||
from nova import test
|
||||
from nova.tests import fixtures
|
||||
|
||||
|
||||
class SupportDirectIOTestCase(test.NoDBTestCase):
|
||||
@ -126,14 +125,3 @@ class SupportDirectIOTestCase(test.NoDBTestCase):
|
||||
self.mock_write.assert_not_called()
|
||||
self.mock_close.assert_not_called()
|
||||
self.mock_unlink.assert_called_once_with(self.test_path)
|
||||
|
||||
|
||||
class UtilsTestCase(test.NoDBTestCase):
|
||||
def setUp(self):
|
||||
super(UtilsTestCase, self).setUp()
|
||||
self.useFixture(fixtures.PrivsepFixture())
|
||||
|
||||
@mock.patch('os.kill')
|
||||
def test_kill(self, mock_kill):
|
||||
nova.privsep.utils.kill(42, -9)
|
||||
mock_kill.assert_called_with(42, -9)
|
||||
|
@ -47,48 +47,6 @@ from nova import utils
|
||||
CONF = cfg.CONF
|
||||
|
||||
|
||||
class TestConfFixture(testtools.TestCase):
|
||||
"""Test the Conf fixtures in Nova.
|
||||
|
||||
This is a basic test that this fixture works like we expect.
|
||||
|
||||
Expectations:
|
||||
|
||||
1. before using the fixture, a default value (api_paste_config)
|
||||
comes through untouched.
|
||||
|
||||
2. before using the fixture, a known default value that we
|
||||
override is correct.
|
||||
|
||||
3. after using the fixture a known value that we override is the
|
||||
new value.
|
||||
|
||||
4. after using the fixture we can set a default value to something
|
||||
random, and it will be reset once we are done.
|
||||
|
||||
There are 2 copies of this test so that you can verify they do the
|
||||
right thing with:
|
||||
|
||||
tox -e py27 test_fixtures -- --concurrency=1
|
||||
|
||||
As regardless of run order, their initial asserts would be
|
||||
impacted if the reset behavior isn't working correctly.
|
||||
|
||||
"""
|
||||
def _test_override(self):
|
||||
self.assertEqual('api-paste.ini', CONF.wsgi.api_paste_config)
|
||||
self.assertFalse(CONF.fake_network)
|
||||
self.useFixture(conf_fixture.ConfFixture())
|
||||
CONF.set_default('api_paste_config', 'foo', group='wsgi')
|
||||
self.assertTrue(CONF.fake_network)
|
||||
|
||||
def test_override1(self):
|
||||
self._test_override()
|
||||
|
||||
def test_override2(self):
|
||||
self._test_override()
|
||||
|
||||
|
||||
class TestLogging(testtools.TestCase):
|
||||
def test_default_logging(self):
|
||||
stdlog = self.useFixture(fixtures.StandardLogging())
|
||||
|
@ -1,276 +0,0 @@
|
||||
# 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.
|
||||
"""Unit Tests for network code."""
|
||||
|
||||
import mock
|
||||
import six
|
||||
|
||||
from nova.network import linux_net
|
||||
from nova import test
|
||||
|
||||
|
||||
class IptablesManagerTestCase(test.NoDBTestCase):
|
||||
|
||||
binary_name = linux_net.get_binary_name()
|
||||
|
||||
sample_filter = ['#Generated by iptables-save on Fri Feb 18 15:17:05 2011',
|
||||
'*filter',
|
||||
':INPUT ACCEPT [2223527:305688874]',
|
||||
':FORWARD ACCEPT [0:0]',
|
||||
':OUTPUT ACCEPT [2172501:140856656]',
|
||||
':iptables-top-rule - [0:0]',
|
||||
':iptables-bottom-rule - [0:0]',
|
||||
':%s-FORWARD - [0:0]' % (binary_name),
|
||||
':%s-INPUT - [0:0]' % (binary_name),
|
||||
':%s-OUTPUT - [0:0]' % (binary_name),
|
||||
':%s-local - [0:0]' % (binary_name),
|
||||
':nova-filter-top - [0:0]',
|
||||
'[0:0] -A FORWARD -j nova-filter-top',
|
||||
'[0:0] -A OUTPUT -j nova-filter-top',
|
||||
'[0:0] -A nova-filter-top -j %s-local' % (binary_name),
|
||||
'[0:0] -A INPUT -j %s-INPUT' % (binary_name),
|
||||
'[0:0] -A OUTPUT -j %s-OUTPUT' % (binary_name),
|
||||
'[0:0] -A FORWARD -j %s-FORWARD' % (binary_name),
|
||||
'[0:0] -A INPUT -i virbr0 -p udp -m udp --dport 53 '
|
||||
'-j ACCEPT',
|
||||
'[0:0] -A INPUT -i virbr0 -p tcp -m tcp --dport 53 '
|
||||
'-j ACCEPT',
|
||||
'[0:0] -A INPUT -i virbr0 -p udp -m udp --dport 67 '
|
||||
'-j ACCEPT',
|
||||
'[0:0] -A INPUT -i virbr0 -p tcp -m tcp --dport 67 '
|
||||
'-j ACCEPT',
|
||||
'[0:0] -A FORWARD -s 192.168.122.0/24 -i virbr0 '
|
||||
'-j ACCEPT',
|
||||
'[0:0] -A FORWARD -i virbr0 -o virbr0 -j ACCEPT',
|
||||
'[0:0] -A FORWARD -o virbr0 -j REJECT --reject-with '
|
||||
'icmp-port-unreachable',
|
||||
'[0:0] -A FORWARD -i virbr0 -j REJECT --reject-with '
|
||||
'icmp-port-unreachable',
|
||||
'COMMIT',
|
||||
'# Completed on Fri Feb 18 15:17:05 2011']
|
||||
|
||||
sample_nat = ['# Generated by iptables-save on Fri Feb 18 15:17:05 2011',
|
||||
'*nat',
|
||||
':PREROUTING ACCEPT [3936:762355]',
|
||||
':INPUT ACCEPT [2447:225266]',
|
||||
':OUTPUT ACCEPT [63491:4191863]',
|
||||
':POSTROUTING ACCEPT [63112:4108641]',
|
||||
':%s-OUTPUT - [0:0]' % (binary_name),
|
||||
':%s-POSTROUTING - [0:0]' % (binary_name),
|
||||
':%s-PREROUTING - [0:0]' % (binary_name),
|
||||
':%s-float-snat - [0:0]' % (binary_name),
|
||||
':%s-snat - [0:0]' % (binary_name),
|
||||
':nova-postrouting-bottom - [0:0]',
|
||||
'[0:0] -A PREROUTING -j %s-PREROUTING' % (binary_name),
|
||||
'[0:0] -A OUTPUT -j %s-OUTPUT' % (binary_name),
|
||||
'[0:0] -A POSTROUTING -j %s-POSTROUTING' % (binary_name),
|
||||
'[0:0] -A nova-postrouting-bottom '
|
||||
'-j %s-snat' % (binary_name),
|
||||
'[0:0] -A %s-snat '
|
||||
'-j %s-float-snat' % (binary_name, binary_name),
|
||||
'[0:0] -A POSTROUTING -j nova-postrouting-bottom',
|
||||
'COMMIT',
|
||||
'# Completed on Fri Feb 18 15:17:05 2011']
|
||||
|
||||
def setUp(self):
|
||||
super(IptablesManagerTestCase, self).setUp()
|
||||
self.manager = linux_net.IptablesManager()
|
||||
|
||||
def test_duplicate_rules_no_dirty(self):
|
||||
table = self.manager.ipv4['filter']
|
||||
table.dirty = False
|
||||
num_rules = len(table.rules)
|
||||
table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
||||
self.assertEqual(len(table.rules), num_rules + 1)
|
||||
self.assertTrue(table.dirty)
|
||||
table.dirty = False
|
||||
num_rules = len(table.rules)
|
||||
table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
||||
self.assertEqual(len(table.rules), num_rules)
|
||||
self.assertFalse(table.dirty)
|
||||
|
||||
def test_clean_tables_no_apply(self):
|
||||
for table in six.itervalues(self.manager.ipv4):
|
||||
table.dirty = False
|
||||
for table in six.itervalues(self.manager.ipv6):
|
||||
table.dirty = False
|
||||
|
||||
with mock.patch.object(self.manager, '_apply') as mock_apply:
|
||||
self.manager.apply()
|
||||
self.assertFalse(mock_apply.called)
|
||||
|
||||
def test_filter_rules_are_wrapped(self):
|
||||
current_lines = self.sample_filter
|
||||
|
||||
table = self.manager.ipv4['filter']
|
||||
table.add_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
||||
new_lines = self.manager._modify_rules(current_lines, table, 'filter')
|
||||
self.assertIn('[0:0] -A %s-FORWARD '
|
||||
'-s 1.2.3.4/5 -j DROP' % self.binary_name, new_lines)
|
||||
|
||||
table.remove_rule('FORWARD', '-s 1.2.3.4/5 -j DROP')
|
||||
new_lines = self.manager._modify_rules(current_lines, table, 'filter')
|
||||
self.assertNotIn('[0:0] -A %s-FORWARD '
|
||||
'-s 1.2.3.4/5 -j DROP' % self.binary_name, new_lines)
|
||||
|
||||
def test_remove_rules_regex(self):
|
||||
current_lines = self.sample_nat
|
||||
table = self.manager.ipv4['nat']
|
||||
table.add_rule('float-snat', '-s 10.0.0.1 -j SNAT --to 10.10.10.10'
|
||||
' -d 10.0.0.1')
|
||||
table.add_rule('float-snat', '-s 10.0.0.1 -j SNAT --to 10.10.10.10'
|
||||
' -o eth0')
|
||||
table.add_rule('PREROUTING', '-d 10.10.10.10 -j DNAT --to 10.0.0.1')
|
||||
table.add_rule('OUTPUT', '-d 10.10.10.10 -j DNAT --to 10.0.0.1')
|
||||
table.add_rule('float-snat', '-s 10.0.0.10 -j SNAT --to 10.10.10.11'
|
||||
' -d 10.0.0.10')
|
||||
table.add_rule('float-snat', '-s 10.0.0.10 -j SNAT --to 10.10.10.11'
|
||||
' -o eth0')
|
||||
table.add_rule('PREROUTING', '-d 10.10.10.11 -j DNAT --to 10.0.0.10')
|
||||
table.add_rule('OUTPUT', '-d 10.10.10.11 -j DNAT --to 10.0.0.10')
|
||||
new_lines = self.manager._modify_rules(current_lines, table, 'nat')
|
||||
self.assertEqual(len(new_lines) - len(current_lines), 8)
|
||||
regex = r'.*\s+%s(/32|\s+|$)'
|
||||
num_removed = table.remove_rules_regex(regex % '10.10.10.10')
|
||||
self.assertEqual(num_removed, 4)
|
||||
new_lines = self.manager._modify_rules(current_lines, table, 'nat')
|
||||
self.assertEqual(len(new_lines) - len(current_lines), 4)
|
||||
num_removed = table.remove_rules_regex(regex % '10.10.10.11')
|
||||
self.assertEqual(num_removed, 4)
|
||||
new_lines = self.manager._modify_rules(current_lines, table, 'nat')
|
||||
self.assertEqual(current_lines, new_lines)
|
||||
|
||||
def test_nat_rules(self):
|
||||
current_lines = self.sample_nat
|
||||
new_lines = self.manager._modify_rules(current_lines,
|
||||
self.manager.ipv4['nat'],
|
||||
'nat')
|
||||
|
||||
for line in [':%s-OUTPUT - [0:0]' % (self.binary_name),
|
||||
':%s-float-snat - [0:0]' % (self.binary_name),
|
||||
':%s-snat - [0:0]' % (self.binary_name),
|
||||
':%s-PREROUTING - [0:0]' % (self.binary_name),
|
||||
':%s-POSTROUTING - [0:0]' % (self.binary_name)]:
|
||||
self.assertIn(line, new_lines, "One of our chains went"
|
||||
" missing.")
|
||||
|
||||
seen_lines = set()
|
||||
for line in new_lines:
|
||||
line = line.strip()
|
||||
self.assertNotIn(line, seen_lines, "Duplicate line: %s" % line)
|
||||
seen_lines.add(line)
|
||||
|
||||
last_postrouting_line = ''
|
||||
|
||||
for line in new_lines:
|
||||
if line.startswith('[0:0] -A POSTROUTING'):
|
||||
last_postrouting_line = line
|
||||
|
||||
self.assertIn('-j nova-postrouting-bottom', last_postrouting_line,
|
||||
"Last POSTROUTING rule does not jump to "
|
||||
"nova-postouting-bottom: %s" % last_postrouting_line)
|
||||
|
||||
for chain in ['POSTROUTING', 'PREROUTING', 'OUTPUT']:
|
||||
self.assertTrue('[0:0] -A %s -j %s-%s' %
|
||||
(chain, self.binary_name, chain) in new_lines,
|
||||
"Built-in chain %s not wrapped" % (chain,))
|
||||
|
||||
def test_filter_rules(self):
|
||||
current_lines = self.sample_filter
|
||||
new_lines = self.manager._modify_rules(current_lines,
|
||||
self.manager.ipv4['filter'],
|
||||
'nat')
|
||||
|
||||
for line in [':%s-FORWARD - [0:0]' % (self.binary_name),
|
||||
':%s-INPUT - [0:0]' % (self.binary_name),
|
||||
':%s-local - [0:0]' % (self.binary_name),
|
||||
':%s-OUTPUT - [0:0]' % (self.binary_name)]:
|
||||
self.assertIn(line, new_lines, "One of our chains went"
|
||||
" missing.")
|
||||
|
||||
seen_lines = set()
|
||||
for line in new_lines:
|
||||
line = line.strip()
|
||||
self.assertNotIn(line, seen_lines, "Duplicate line: %s" % line)
|
||||
seen_lines.add(line)
|
||||
|
||||
for chain in ['FORWARD', 'OUTPUT']:
|
||||
for line in new_lines:
|
||||
if line.startswith('[0:0] -A %s' % chain):
|
||||
self.assertIn('-j nova-filter-top', line,
|
||||
"First %s rule does not "
|
||||
"jump to nova-filter-top" % chain)
|
||||
break
|
||||
|
||||
self.assertTrue('[0:0] -A nova-filter-top '
|
||||
'-j %s-local' % self.binary_name in new_lines,
|
||||
"nova-filter-top does not jump to wrapped local chain")
|
||||
|
||||
for chain in ['INPUT', 'OUTPUT', 'FORWARD']:
|
||||
self.assertTrue('[0:0] -A %s -j %s-%s' %
|
||||
(chain, self.binary_name, chain) in new_lines,
|
||||
"Built-in chain %s not wrapped" % (chain,))
|
||||
|
||||
def test_missing_table(self):
|
||||
current_lines = []
|
||||
new_lines = self.manager._modify_rules(current_lines,
|
||||
self.manager.ipv4['filter'],
|
||||
'filter')
|
||||
|
||||
for line in ['*filter',
|
||||
'COMMIT']:
|
||||
self.assertIn(line, new_lines, "One of iptables key lines "
|
||||
"went missing.")
|
||||
|
||||
self.assertGreater(len(new_lines), 4, "No iptables rules added")
|
||||
|
||||
msg = "iptables rules not generated in the correct order"
|
||||
self.assertEqual("#Generated by nova", new_lines[0], msg)
|
||||
self.assertEqual("*filter", new_lines[1], msg)
|
||||
self.assertEqual("COMMIT", new_lines[-2], msg)
|
||||
self.assertEqual("#Completed by nova", new_lines[-1], msg)
|
||||
|
||||
def test_iptables_top_order(self):
|
||||
# Test iptables_top_regex
|
||||
current_lines = list(self.sample_filter)
|
||||
current_lines[12:12] = ['[0:0] -A FORWARD -j iptables-top-rule']
|
||||
self.flags(iptables_top_regex='-j iptables-top-rule')
|
||||
new_lines = self.manager._modify_rules(current_lines,
|
||||
self.manager.ipv4['filter'],
|
||||
'filter')
|
||||
self.assertEqual(current_lines, new_lines)
|
||||
|
||||
def test_iptables_bottom_order(self):
|
||||
# Test iptables_bottom_regex
|
||||
current_lines = list(self.sample_filter)
|
||||
current_lines[26:26] = ['[0:0] -A FORWARD -j iptables-bottom-rule']
|
||||
self.flags(iptables_bottom_regex='-j iptables-bottom-rule')
|
||||
new_lines = self.manager._modify_rules(current_lines,
|
||||
self.manager.ipv4['filter'],
|
||||
'filter')
|
||||
self.assertEqual(current_lines, new_lines)
|
||||
|
||||
def test_iptables_preserve_order(self):
|
||||
# Test both iptables_top_regex and iptables_bottom_regex
|
||||
current_lines = list(self.sample_filter)
|
||||
current_lines[12:12] = ['[0:0] -A FORWARD -j iptables-top-rule']
|
||||
current_lines[27:27] = ['[0:0] -A FORWARD -j iptables-bottom-rule']
|
||||
self.flags(iptables_top_regex='-j iptables-top-rule')
|
||||
self.flags(iptables_bottom_regex='-j iptables-bottom-rule')
|
||||
new_lines = self.manager._modify_rules(current_lines,
|
||||
self.manager.ipv4['filter'],
|
||||
'filter')
|
||||
self.assertEqual(current_lines, new_lines)
|
@ -1,82 +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.
|
||||
|
||||
"""Test suite for IPv6."""
|
||||
|
||||
from nova import ipv6
|
||||
from nova import test
|
||||
|
||||
|
||||
class IPv6RFC2462TestCase(test.NoDBTestCase):
|
||||
"""Unit tests for IPv6 rfc2462 backend operations."""
|
||||
def setUp(self):
|
||||
super(IPv6RFC2462TestCase, self).setUp()
|
||||
self.flags(ipv6_backend='rfc2462')
|
||||
ipv6.reset_backend()
|
||||
|
||||
def test_to_global(self):
|
||||
addr = ipv6.to_global('2001:db8::', '02:16:3e:33:44:55', 'test')
|
||||
self.assertEqual(addr, '2001:db8::16:3eff:fe33:4455')
|
||||
|
||||
def test_to_mac(self):
|
||||
mac = ipv6.to_mac('2001:db8::216:3eff:fe33:4455')
|
||||
self.assertEqual(mac, '00:16:3e:33:44:55')
|
||||
|
||||
def test_to_global_with_bad_mac(self):
|
||||
bad_mac = '02:16:3e:33:44:5Z'
|
||||
expected_msg = 'Bad mac for to_global_ipv6: %s' % bad_mac
|
||||
err = self.assertRaises(TypeError, ipv6.to_global,
|
||||
'2001:db8::', bad_mac, 'test')
|
||||
self.assertEqual(expected_msg, str(err))
|
||||
|
||||
def test_to_global_with_bad_prefix(self):
|
||||
bad_prefix = '2001::1::2'
|
||||
expected_msg = 'Bad prefix for to_global_ipv6: %s' % bad_prefix
|
||||
err = self.assertRaises(TypeError, ipv6.to_global,
|
||||
bad_prefix,
|
||||
'02:16:3e:33:44:55',
|
||||
'test')
|
||||
self.assertEqual(expected_msg, str(err))
|
||||
|
||||
|
||||
class IPv6AccountIdentiferTestCase(test.NoDBTestCase):
|
||||
"""Unit tests for IPv6 account_identifier backend operations."""
|
||||
def setUp(self):
|
||||
super(IPv6AccountIdentiferTestCase, self).setUp()
|
||||
self.flags(ipv6_backend='account_identifier')
|
||||
ipv6.reset_backend()
|
||||
|
||||
def test_to_global(self):
|
||||
addr = ipv6.to_global('2001:db8::', '02:16:3e:33:44:55', 'test')
|
||||
self.assertEqual(addr, '2001:db8::a94a:8fe5:ff33:4455')
|
||||
|
||||
def test_to_mac(self):
|
||||
mac = ipv6.to_mac('2001:db8::a94a:8fe5:ff33:4455')
|
||||
self.assertEqual(mac, '02:16:3e:33:44:55')
|
||||
|
||||
def test_to_global_with_bad_mac(self):
|
||||
bad_mac = '02:16:3e:33:44:5Z'
|
||||
expected_msg = 'Bad mac for to_global_ipv6: %s' % bad_mac
|
||||
err = self.assertRaises(TypeError, ipv6.to_global,
|
||||
'2001:db8::', bad_mac, 'test')
|
||||
self.assertEqual(expected_msg, str(err))
|
||||
|
||||
def test_to_global_with_bad_prefix(self):
|
||||
bad_prefix = '2001::1::2'
|
||||
expected_msg = 'Bad prefix for to_global_ipv6: %s' % bad_prefix
|
||||
err = self.assertRaises(TypeError, ipv6.to_global,
|
||||
bad_prefix,
|
||||
'02:16:3e:33:44:55',
|
||||
'test')
|
||||
self.assertEqual(expected_msg, str(err))
|
@ -55,12 +55,7 @@ class TestProfiler(test.NoDBTestCase):
|
||||
'nova.conductor.rpcapi.ComputeTaskAPI',
|
||||
'nova.conductor.rpcapi.ConductorAPI',
|
||||
'nova.image.api.API',
|
||||
'nova.network.api.API',
|
||||
'nova.network.manager.FlatDHCPManager',
|
||||
'nova.network.manager.FlatManager',
|
||||
'nova.network.manager.VlanManager',
|
||||
'nova.network.neutronv2.api.ClientWrapper',
|
||||
'nova.network.rpcapi.NetworkAPI',
|
||||
'nova.scheduler.manager.SchedulerManager',
|
||||
'nova.scheduler.rpcapi.SchedulerAPI',
|
||||
'nova.virt.libvirt.vif.LibvirtGenericVIFDriver',
|
||||
|
@ -27,7 +27,6 @@ import nova.context
|
||||
from nova.db import api as db
|
||||
from nova import exception
|
||||
from nova.image import glance
|
||||
from nova.network import minidns
|
||||
from nova.network import model as network_model
|
||||
from nova import objects
|
||||
from nova.objects import base as obj_base
|
||||
@ -135,8 +134,6 @@ FAKE_VIF_MAC = 'de:ad:be:ef:ca:fe'
|
||||
|
||||
|
||||
def get_test_network_info(count=1):
|
||||
ipv6 = CONF.use_ipv6
|
||||
|
||||
def current():
|
||||
subnet_4 = network_model.Subnet(
|
||||
cidr=FAKE_NETWORK_IP4_CIDR,
|
||||
@ -155,9 +152,7 @@ def get_test_network_info(count=1):
|
||||
network_model.IP(FAKE_NETWORK_IP6_ADDR3)],
|
||||
routes=None,
|
||||
version=6)
|
||||
subnets = [subnet_4]
|
||||
if ipv6:
|
||||
subnets.append(subnet_6)
|
||||
subnets = [subnet_4, subnet_6]
|
||||
network = network_model.Network(
|
||||
id=FAKE_NETWORK_UUID,
|
||||
bridge=FAKE_NETWORK_BRIDGE,
|
||||
@ -187,23 +182,6 @@ def is_linux():
|
||||
return platform.system() == 'Linux'
|
||||
|
||||
|
||||
test_dns_managers = []
|
||||
|
||||
|
||||
def dns_manager():
|
||||
global test_dns_managers
|
||||
manager = minidns.MiniDNS()
|
||||
test_dns_managers.append(manager)
|
||||
return manager
|
||||
|
||||
|
||||
def cleanup_dns_managers():
|
||||
global test_dns_managers
|
||||
for manager in test_dns_managers:
|
||||
manager.delete_dns_file()
|
||||
test_dns_managers = []
|
||||
|
||||
|
||||
def killer_xml_body():
|
||||
return (("""<!DOCTYPE x [
|
||||
<!ENTITY a "%(a)s">
|
||||
|
@ -53,7 +53,6 @@ from six.moves import range
|
||||
import nova.conf
|
||||
from nova import exception
|
||||
from nova.i18n import _, _LE, _LW
|
||||
import nova.network
|
||||
from nova import safe_utils
|
||||
|
||||
profiler = importutils.try_import('osprofiler.profiler')
|
||||
@ -63,8 +62,6 @@ CONF = nova.conf.CONF
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
_IS_NEUTRON = None
|
||||
|
||||
synchronized = lockutils.synchronized_with_prefix('nova-')
|
||||
|
||||
SM_IMAGE_PROP_PREFIX = "image_"
|
||||
@ -709,16 +706,6 @@ def is_none_string(val):
|
||||
return val.lower() == 'none'
|
||||
|
||||
|
||||
def is_neutron():
|
||||
global _IS_NEUTRON
|
||||
|
||||
if _IS_NEUTRON is not None:
|
||||
return _IS_NEUTRON
|
||||
|
||||
_IS_NEUTRON = nova.network.is_neutron()
|
||||
return _IS_NEUTRON
|
||||
|
||||
|
||||
def is_auto_disk_config_disabled(auto_disk_config_raw):
|
||||
auto_disk_config_disabled = False
|
||||
if auto_disk_config_raw is not None:
|
||||
|
@ -61,4 +61,76 @@ upgrade:
|
||||
|
||||
* ``[DEFAULT] firewall_driver``
|
||||
* ``[DEFAULT] allow_same_net_traffic``
|
||||
* ``[DEFAULT] flat_network_bridge``
|
||||
* ``[DEFAULT] flat_network_dns``
|
||||
* ``[DEFAULT] flat_interface``
|
||||
* ``[DEFAULT] vlan_interface``
|
||||
* ``[DEFAULT] vlan_start``
|
||||
* ``[DEFAULT] num_networks``
|
||||
* ``[DEFAULT] vpn_ip``
|
||||
* ``[DEFAULT] vpn_start``
|
||||
* ``[DEFAULT] network_size``
|
||||
* ``[DEFAULT] fixed_range_v6``
|
||||
* ``[DEFAULT] gateway``
|
||||
* ``[DEFAULT] gateway_v6``
|
||||
* ``[DEFAULT] cnt_vpn_clients``
|
||||
* ``[DEFAULT] fixed_ip_disassociate_timeout``
|
||||
* ``[DEFAULT] create_unique_mac_address_attempts``
|
||||
* ``[DEFAULT] teardown_unused_network_gateway``
|
||||
* ``[DEFAULT] l3_lib``
|
||||
* ``[DEFAULT] network_driver``
|
||||
* ``[DEFAULT] network_manager``
|
||||
* ``[DEFAULT] multi_host``
|
||||
* ``[DEFAULT] force_dhcp_release``
|
||||
* ``[DEFAULT] update_dns_entries``
|
||||
* ``[DEFAULT] dns_update_periodic_interval``
|
||||
* ``[DEFAULT] dhcp_domain``
|
||||
* ``[DEFAULT] use_neutron``
|
||||
* ``[DEFAULT] auto_assign_floating_ip``
|
||||
* ``[DEFAULT] floating_ip_dns_manager``
|
||||
* ``[DEFAULT] instance_dns_manager``
|
||||
* ``[DEFAULT] instance_dns_domain``
|
||||
* ``[DEFAULT] default_floating_pool``
|
||||
* ``[DEFAULT] ipv6_backend``
|
||||
* ``[DEFAULT] metadata_host``
|
||||
* ``[DEFAULT] metadata_port``
|
||||
* ``[DEFAULT] iptables_top_regex``
|
||||
* ``[DEFAULT] iptables_bottom_regex``
|
||||
* ``[DEFAULT] iptables_drop_action``
|
||||
* ``[DEFAULT] ldap_dns_url``
|
||||
* ``[DEFAULT] ldap_dns_user``
|
||||
* ``[DEFAULT] ldap_dns_password``
|
||||
* ``[DEFAULT] ldap_dns_soa_hostmaster``
|
||||
* ``[DEFAULT] ldap_dns_servers``
|
||||
* ``[DEFAULT] ldap_dns_base_dn``
|
||||
* ``[DEFAULT] ldap_dns_soa_refresh``
|
||||
* ``[DEFAULT] ldap_dns_soa_retry``
|
||||
* ``[DEFAULT] ldap_dns_soa_expiry``
|
||||
* ``[DEFAULT] ldap_dns_soa_minimum``
|
||||
* ``[DEFAULT] dhcpbridge_flagfile``
|
||||
* ``[DEFAULT] dhcpbridge``
|
||||
* ``[DEFAULT] dhcp_lease_time``
|
||||
* ``[DEFAULT] dns_server``
|
||||
* ``[DEFAULT] use_network_dns_servers``
|
||||
* ``[DEFAULT] dnsmasq_config_file``
|
||||
* ``[DEFAULT] ebtables_exec_attempts``
|
||||
* ``[DEFAULT] ebtables_retry_interval``
|
||||
* ``[DEFAULT] fake_network``
|
||||
* ``[DEFAULT] send_arp_for_ha``
|
||||
* ``[DEFAULT] send_arp_for_ha_count``
|
||||
* ``[DEFAULT] dmz_cidr``
|
||||
* ``[DEFAULT] force_snat_range``
|
||||
* ``[DEFAULT] linuxnet_interface_driver``
|
||||
* ``[DEFAULT] linuxnet_ovs_integration_bridge``
|
||||
* ``[DEFAULT] use_single_default_gateway``
|
||||
* ``[DEFAULT] forward_bridge_interface``
|
||||
* ``[DEFAULT] ovs_vsctl_timeout``
|
||||
* ``[DEFAULT] networks_path``
|
||||
* ``[DEFAULT] public_interface``
|
||||
* ``[DEFAULT] routing_source_ip``
|
||||
* ``[DEFAULT] use_ipv6``
|
||||
* ``[DEFAULT] allow_same_net_traffic``
|
||||
* ``[DEFAULT] defer_iptables_apply``
|
||||
* ``[DEFAULT] share_dhcp_address``
|
||||
* ``[upgrade_levels] network``
|
||||
* ``[vmware] vlan_interface``
|
||||
|
11
setup.cfg
11
setup.cfg
@ -36,31 +36,21 @@ packages =
|
||||
[entry_points]
|
||||
oslo.config.opts =
|
||||
nova.conf = nova.conf.opts:list_opts
|
||||
|
||||
oslo.config.opts.defaults =
|
||||
nova.conf = nova.middleware:set_defaults
|
||||
|
||||
oslo.policy.enforcer =
|
||||
nova = nova.policy:get_enforcer
|
||||
|
||||
oslo.policy.policies =
|
||||
# The sample policies will be ordered by entry point and then by list
|
||||
# returned from that entry point. If more control is desired split out each
|
||||
# list_rules method into a separate entry point rather than using the
|
||||
# aggregate method.
|
||||
nova = nova.policies:list_rules
|
||||
|
||||
nova.compute.monitors.cpu =
|
||||
virt_driver = nova.compute.monitors.cpu.virt_driver:Monitor
|
||||
|
||||
nova.ipv6_backend =
|
||||
rfc2462 = nova.ipv6.rfc2462
|
||||
account_identifier = nova.ipv6.account_identifier
|
||||
|
||||
nova.scheduler.driver =
|
||||
filter_scheduler = nova.scheduler.filter_scheduler:FilterScheduler
|
||||
fake_scheduler = nova.tests.unit.scheduler.fakes:FakeScheduler
|
||||
|
||||
console_scripts =
|
||||
nova-api = nova.cmd.api:main
|
||||
nova-api-metadata = nova.cmd.api_metadata:main
|
||||
@ -76,7 +66,6 @@ console_scripts =
|
||||
nova-serialproxy = nova.cmd.serialproxy:main
|
||||
nova-spicehtml5proxy = nova.cmd.spicehtml5proxy:main
|
||||
nova-status = nova.cmd.status:main
|
||||
|
||||
wsgi_scripts =
|
||||
nova-api-wsgi = nova.api.openstack.compute.wsgi:init_application
|
||||
nova-metadata-wsgi = nova.api.metadata.wsgi:init_application
|
||||
|
@ -7,7 +7,6 @@ coverage!=4.4,>=4.0 # Apache-2.0
|
||||
ddt>=1.0.1 # MIT
|
||||
fixtures>=3.0.0 # Apache-2.0/BSD
|
||||
mock>=3.0.0 # BSD
|
||||
mox3>=0.20.0 # Apache-2.0
|
||||
psycopg2>=2.7 # LGPL/ZPL
|
||||
PyMySQL>=0.7.6 # MIT License
|
||||
pycodestyle>=2.0.0 # MIT License
|
||||
|
Loading…
Reference in New Issue
Block a user