summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorZuul <zuul@review.openstack.org>2018-01-23 16:00:50 +0000
committerGerrit Code Review <review@openstack.org>2018-01-23 16:00:50 +0000
commit6ee297f7a586845c89ade7b826ddce8226016fa2 (patch)
tree95cf9692576c3bf441474752ce6fd2c6edd3199b
parent83e3afbdee95ad5e01d790f54da25653decf2b25 (diff)
parentfbee0981ad0afe304e58006609eabf35cfe89489 (diff)
downloadironic-6ee297f7a586845c89ade7b826ddce8226016fa2.tar.gz
Merge "Add rescue related methods to network interface"
-rw-r--r--etc/ironic/ironic.conf.sample162
-rw-r--r--ironic/common/neutron.py10
-rw-r--r--ironic/conf/neutron.py20
-rw-r--r--ironic/drivers/base.py27
-rw-r--r--ironic/drivers/modules/network/common.py6
-rw-r--r--ironic/drivers/modules/network/neutron.py40
-rw-r--r--ironic/drivers/modules/network/noop.py5
-rw-r--r--ironic/tests/base.py2
-rw-r--r--ironic/tests/unit/common/test_network.py3
-rw-r--r--ironic/tests/unit/drivers/modules/network/test_common.py11
-rw-r--r--ironic/tests/unit/drivers/modules/network/test_neutron.py88
11 files changed, 334 insertions, 40 deletions
diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample
index 03eb8b530..e14443de9 100644
--- a/etc/ironic/ironic.conf.sample
+++ b/etc/ironic/ironic.conf.sample
@@ -7,7 +7,9 @@
# Authentication strategy used by ironic-api. "noauth" should
# not be used in a production environment because all
# authentication will be disabled. (string value)
-# Allowed values: noauth, keystone
+# Possible values:
+# noauth - <No description provided>
+# keystone - <No description provided>
#auth_strategy = keystone
# Return server tracebacks in the API response for any error
@@ -362,7 +364,12 @@
# Specifies the minimum level for which to send notifications.
# If not set, no notifications will be sent. The default is
# for this option to be unset. (string value)
-# Allowed values: debug, info, warning, error, critical
+# Possible values:
+# debug - <No description provided>
+# info - <No description provided>
+# warning - <No description provided>
+# error - <No description provided>
+# critical - <No description provided>
#notification_level = <None>
# Directory where the ironic python module is installed.
@@ -397,7 +404,13 @@
# doing a rolling upgrade from version N to version N+1, set
# (to pin) this to N. To unpin (default), leave it unset and
# the latest versions will be used. (string value)
-# Allowed values: pike, 9.2, 9.1, 9.0, 8.0, 10.0
+# Possible values:
+# pike - <No description provided>
+# 9.2 - <No description provided>
+# 9.1 - <No description provided>
+# 9.0 - <No description provided>
+# 8.0 - <No description provided>
+# 10.0 - <No description provided>
#pin_release_version = <None>
# Path to the rootwrap configuration file to use for running
@@ -554,7 +567,10 @@
#rpc_zmq_bind_address = *
# MatchMaker driver. (string value)
-# Allowed values: redis, sentinel, dummy
+# Possible values:
+# redis - <No description provided>
+# sentinel - <No description provided>
+# dummy - <No description provided>
#rpc_zmq_matchmaker = redis
# Number of ZeroMQ contexts, defaults to 1. (integer value)
@@ -630,7 +646,9 @@
# Default serialization mechanism for
# serializing/deserializing outgoing/incoming messages (string
# value)
-# Allowed values: json, msgpack
+# Possible values:
+# json - <No description provided>
+# msgpack - <No description provided>
#rpc_zmq_serialization = json
# This option configures round-robin mode in zmq socket. True
@@ -821,12 +839,17 @@
# Whether Ironic should collect the deployment logs on
# deployment failure (on_failure), always or never. (string
# value)
-# Allowed values: always, on_failure, never
+# Possible values:
+# always - <No description provided>
+# on_failure - <No description provided>
+# never - <No description provided>
#deploy_logs_collect = on_failure
# The name of the storage backend where the logs will be
# stored. (string value)
-# Allowed values: local, swift
+# Possible values:
+# local - <No description provided>
+# swift - <No description provided>
#deploy_logs_storage_backend = local
# The path to the directory where the logs should be stored,
@@ -1596,7 +1619,9 @@
# but it will be changed to "local" in the future. It is
# recommended to set an explicit value for this option.
# (string value)
-# Allowed values: netboot, local
+# Possible values:
+# netboot - <No description provided>
+# local - <No description provided>
#default_boot_option = <None>
# Whether to upload the config drive to object store. Set this
@@ -1607,7 +1632,9 @@
# Type of object store endpoint type to be used as a backend
# (string value)
-# Allowed values: swift, radosgw
+# Possible values:
+# swift - <No description provided>
+# radosgw - <No description provided>
# Deprecated group/name - [glance]/temp_url_endpoint_type
#object_store_endpoint_type = swift
@@ -1694,7 +1721,9 @@
# DEPRECATED: Authentication strategy to use when connecting
# to glance. (string value)
-# Allowed values: keystone, noauth
+# Possible values:
+# keystone - <No description provided>
+# noauth - <No description provided>
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: To configure glance in noauth mode, set
@@ -1999,7 +2028,10 @@
# for backward compatibility. When "auto" is specified,
# default boot mode will be selected based on boot mode
# settings on the system. (string value)
-# Allowed values: auto, bios, uefi
+# Possible values:
+# auto - <No description provided>
+# bios - <No description provided>
+# uefi - <No description provided>
#default_boot_mode = auto
@@ -2191,7 +2223,9 @@
#remote_image_server = <None>
# Share type of virtual media (string value)
-# Allowed values: CIFS, NFS
+# Possible values:
+# CIFS - <No description provided>
+# NFS - <No description provided>
#remote_image_share_type = CIFS
# share name of remote_image_server (string value)
@@ -2209,23 +2243,32 @@
# Port to be used for iRMC operations (port value)
# Minimum value: 0
# Maximum value: 65535
-# Allowed values: 443, 80
+# Possible values:
+# 443 - <No description provided>
+# 80 - <No description provided>
#port = 443
# Authentication method to be used for iRMC operations (string
# value)
-# Allowed values: basic, digest
+# Possible values:
+# basic - <No description provided>
+# digest - <No description provided>
#auth_method = basic
# Timeout (in seconds) for iRMC operations (integer value)
#client_timeout = 60
# Sensor data retrieval method. (string value)
-# Allowed values: ipmitool, scci
+# Possible values:
+# ipmitool - <No description provided>
+# scci - <No description provided>
#sensor_method = ipmitool
# SNMP protocol version (string value)
-# Allowed values: v1, v2c, v3
+# Possible values:
+# v1 - <No description provided>
+# v2c - <No description provided>
+# v3 - <No description provided>
#snmp_version = v2c
# SNMP port (port value)
@@ -2416,7 +2459,10 @@
# token data is encrypted and authenticated in the cache. If
# the value is not one of these options or empty, auth_token
# will raise an exception on initialization. (string value)
-# Allowed values: None, MAC, ENCRYPT
+# Possible values:
+# None - <No description provided>
+# MAC - <No description provided>
+# ENCRYPT - <No description provided>
#memcache_security_strategy = None
# (Optional, mandatory if memcache_security_strategy is
@@ -2603,7 +2649,9 @@
#
# Backend to use for the metrics system. (string value)
-# Allowed values: noop, statsd
+# Possible values:
+# noop - <No description provided>
+# statsd - <No description provided>
#backend = noop
# Prepend the hostname to all metric names. The format of
@@ -2667,7 +2715,9 @@
# to neutron. Running neutron in noauth mode (related to but
# not affected by this setting) is insecure and should only be
# used for testing. (string value)
-# Allowed values: keystone, noauth
+# Possible values:
+# keystone - <No description provided>
+# noauth - <No description provided>
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: To configure neutron for noauth mode, set
@@ -2782,6 +2832,25 @@
# value)
#region_name = <None>
+# Neutron network UUID or name for booting the ramdisk for
+# rescue mode. This is not the network that the rescue ramdisk
+# will use post-boot -- the tenant network is used for that.
+# Required for "neutron" network interface, if rescue mode
+# will be used. It is not used for the "flat" or "noop"
+# network interfaces. If a name is provided, it must be unique
+# among all networks or rescue will fail. This option is part
+# of rescue feature work, which is not currently exposed to
+# users. (string value)
+#rescuing_network = <None>
+
+# List of Neutron Security Group UUIDs to be applied during
+# the node rescue process. Optional for the "neutron" network
+# interface and not used for the "flat" or "noop" network
+# interfaces. If not specified, the default security group is
+# used. This option is part of rescue feature work, which is
+# not currently exposed to users. (list value)
+#rescuing_network_security_groups =
+
# Client retries in the case of a failed request. (integer
# value)
#retries = 3
@@ -3164,15 +3233,24 @@
# value)
#kafka_consumer_timeout = 1.0
-# Pool Size for Kafka Consumers (integer value)
+# DEPRECATED: Pool Size for Kafka Consumers (integer value)
+# This option is deprecated for removal.
+# Its value may be silently ignored in the future.
+# Reason: Driver no longer uses connection pool.
#pool_size = 10
-# The pool size limit for connections expiration policy
-# (integer value)
+# DEPRECATED: The pool size limit for connections expiration
+# policy (integer value)
+# This option is deprecated for removal.
+# Its value may be silently ignored in the future.
+# Reason: Driver no longer uses connection pool.
#conn_pool_min_size = 2
-# The time-to-live in sec of idle connections in the pool
-# (integer value)
+# DEPRECATED: The time-to-live in sec of idle connections in
+# the pool (integer value)
+# This option is deprecated for removal.
+# Its value may be silently ignored in the future.
+# Reason: Driver no longer uses connection pool.
#conn_pool_ttl = 1200
# Group id for Kafka consumer. Consumers in one group will
@@ -3271,7 +3349,9 @@
# one we are currently connected to becomes unavailable. Takes
# effect only if more than one RabbitMQ node is provided in
# config. (string value)
-# Allowed values: round-robin, shuffle
+# Possible values:
+# round-robin - <No description provided>
+# shuffle - <No description provided>
#kombu_failover_strategy = round-robin
# DEPRECATED: The RabbitMQ broker address where a single node
@@ -3310,7 +3390,10 @@
#rabbit_password = guest
# The RabbitMQ login method. (string value)
-# Allowed values: PLAIN, AMQPLAIN, RABBIT-CR-DEMO
+# Possible values:
+# PLAIN - <No description provided>
+# AMQPLAIN - <No description provided>
+# RABBIT-CR-DEMO - <No description provided>
#rabbit_login_method = AMQPLAIN
# DEPRECATED: The RabbitMQ virtual host. (string value)
@@ -3397,7 +3480,10 @@
#host_connection_reconnect_delay = 0.25
# Connection factory implementation (string value)
-# Allowed values: new, single, read_write
+# Possible values:
+# new - <No description provided>
+# single - <No description provided>
+# read_write - <No description provided>
#connection_factory = single
# Maximum number of connections to keep queued. (integer
@@ -3425,7 +3511,9 @@
# Default serialization mechanism for
# serializing/deserializing outgoing/incoming messages (string
# value)
-# Allowed values: json, msgpack
+# Possible values:
+# json - <No description provided>
+# msgpack - <No description provided>
#default_serializer_type = json
# Persist notification messages. (boolean value)
@@ -3497,7 +3585,10 @@
#rpc_zmq_bind_address = *
# MatchMaker driver. (string value)
-# Allowed values: redis, sentinel, dummy
+# Possible values:
+# redis - <No description provided>
+# sentinel - <No description provided>
+# dummy - <No description provided>
#rpc_zmq_matchmaker = redis
# Number of ZeroMQ contexts, defaults to 1. (integer value)
@@ -3573,7 +3664,9 @@
# Default serialization mechanism for
# serializing/deserializing outgoing/incoming messages (string
# value)
-# Allowed values: json, msgpack
+# Possible values:
+# json - <No description provided>
+# msgpack - <No description provided>
#rpc_zmq_serialization = json
# This option configures round-robin mode in zmq socket. True
@@ -3682,7 +3775,10 @@
# Content Type to send and receive data for REST based policy
# check (string value)
-# Allowed values: application/x-www-form-urlencoded, application/json
+# Possible values:
+# application/x-www-form-urlencoded - <No description
+# provided>
+# application/json - <No description provided>
#remote_content_type = application/x-www-form-urlencoded
# server identity verification for REST based policy check
@@ -3914,7 +4010,9 @@
# The IP version that will be used for PXE booting. Defaults
# to 4. EXPERIMENTAL (string value)
-# Allowed values: 4, 6
+# Possible values:
+# 4 - <No description provided>
+# 6 - <No description provided>
#ip_version = 4
# Download deploy images directly from swift using temporary
diff --git a/ironic/common/neutron.py b/ironic/common/neutron.py
index aa30c0a50..aecf73591 100644
--- a/ironic/common/neutron.py
+++ b/ironic/common/neutron.py
@@ -566,6 +566,7 @@ class NeutronNetworkInterfaceMixin(object):
_cleaning_network_uuid = None
_provisioning_network_uuid = None
+ _rescuing_network_uuid = None
def get_cleaning_network_uuid(self, context=None):
if self._cleaning_network_uuid is None:
@@ -580,3 +581,12 @@ class NeutronNetworkInterfaceMixin(object):
CONF.neutron.provisioning_network,
_('provisioning network'), context=context)
return self._provisioning_network_uuid
+
+ def get_rescuing_network_uuid(self, context=None):
+ # TODO(stendulker): FlatNetwork should not use this method.
+ # FlatNetwork uses tenant network for rescue operation.
+ if self._rescuing_network_uuid is None:
+ self._rescuing_network_uuid = validate_network(
+ CONF.neutron.rescuing_network,
+ _('rescuing network'), context=context)
+ return self._rescuing_network_uuid
diff --git a/ironic/conf/neutron.py b/ironic/conf/neutron.py
index 86d0fca1b..1d1227cd4 100644
--- a/ironic/conf/neutron.py
+++ b/ironic/conf/neutron.py
@@ -90,6 +90,26 @@ opts = [
'used for the "flat" or "noop" network interfaces. '
'If not specified, default security group '
'is used.')),
+ cfg.StrOpt('rescuing_network',
+ help=_('Neutron network UUID or name for booting the ramdisk '
+ 'for rescue mode. This is not the network that the '
+ 'rescue ramdisk will use post-boot -- the tenant '
+ 'network is used for that. Required for "neutron" '
+ 'network interface, if rescue mode will be used. It '
+ 'is not used for the "flat" or "noop" network '
+ 'interfaces. If a name is provided, it must be unique '
+ 'among all networks or rescue will fail. This option '
+ 'is part of rescue feature work, which is not currently '
+ 'exposed to users.')),
+ cfg.ListOpt('rescuing_network_security_groups',
+ default=[],
+ help=_('List of Neutron Security Group UUIDs to be applied '
+ 'during the node rescue process. Optional for the '
+ '"neutron" network interface and not used for the '
+ '"flat" or "noop" network interfaces. If not '
+ 'specified, the default security group is used. This '
+ 'option is part of rescue feature work, which is '
+ 'not currently exposed to users.')),
]
diff --git a/ironic/drivers/base.py b/ironic/drivers/base.py
index 1f1e4d2b9..ab00736ec 100644
--- a/ironic/drivers/base.py
+++ b/ironic/drivers/base.py
@@ -1036,8 +1036,9 @@ class NetworkInterface(BaseInterface):
"""Returns the currently used VIF associated with port or portgroup
We are booting the node only in one network at a time, and presence of
- cleaning_vif_port_id means we're doing cleaning, of
- provisioning_vif_port_id - provisioning.
+ cleaning_vif_port_id means we're doing cleaning,
+ of provisioning_vif_port_id - provisioning,
+ of rescuing_vif_port_id - rescuing.
Otherwise it's a tenant network.
:param task: A TaskManager instance.
@@ -1092,6 +1093,28 @@ class NetworkInterface(BaseInterface):
:raises: NetworkError
"""
+ def add_rescuing_network(self, task):
+ """Add the rescuing network to the node.
+
+ :param task: A TaskManager instance.
+ :returns: a dictionary in the form {port.uuid: neutron_port['id']}
+ :raises: NetworkError
+ :raises: InvalidParameterValue, if the network interface configuration
+ is invalid.
+ """
+ return {}
+
+ def remove_rescuing_network(self, task):
+ """Removes the rescuing network from a node.
+
+ :param task: A TaskManager instance.
+ :raises: NetworkError
+ :raises: InvalidParameterValue, if the network interface configuration
+ is invalid.
+ :raises: MissingParameterValue, if some parameters are missing.
+ """
+ pass
+
@six.add_metaclass(abc.ABCMeta)
class StorageInterface(BaseInterface):
diff --git a/ironic/drivers/modules/network/common.py b/ironic/drivers/modules/network/common.py
index 563f09130..dd99da141 100644
--- a/ironic/drivers/modules/network/common.py
+++ b/ironic/drivers/modules/network/common.py
@@ -362,8 +362,9 @@ class VIFPortIDMixin(object):
"""Returns the currently used VIF associated with port or portgroup
We are booting the node only in one network at a time, and presence of
- cleaning_vif_port_id means we're doing cleaning, of
- provisioning_vif_port_id - provisioning.
+ cleaning_vif_port_id means we're doing cleaning,
+ of provisioning_vif_port_id - provisioning,
+ of rescuing_vif_port_id - rescuing.
Otherwise it's a tenant network
:param task: A TaskManager instance.
@@ -373,6 +374,7 @@ class VIFPortIDMixin(object):
return (p_obj.internal_info.get('cleaning_vif_port_id') or
p_obj.internal_info.get('provisioning_vif_port_id') or
+ p_obj.internal_info.get('rescuing_vif_port_id') or
self._get_vif_id_by_port_like_obj(p_obj) or None)
diff --git a/ironic/drivers/modules/network/neutron.py b/ironic/drivers/modules/network/neutron.py
index b88b47703..0f4048cc5 100644
--- a/ironic/drivers/modules/network/neutron.py
+++ b/ironic/drivers/modules/network/neutron.py
@@ -140,6 +140,46 @@ class NeutronNetwork(common.NeutronVIFPortIDMixin,
port.internal_info = internal_info
port.save()
+ def add_rescuing_network(self, task):
+ """Create neutron ports for each port to boot the rescue ramdisk.
+
+ :param task: a TaskManager instance.
+ :returns: a dictionary in the form {port.uuid: neutron_port['id']}
+ """
+ # If we have left over ports from a previous rescue, remove them
+ neutron.rollback_ports(task, self.get_rescuing_network_uuid(
+ context=task.context))
+ LOG.info('Adding rescuing network to node %s', task.node.uuid)
+ security_groups = CONF.neutron.rescuing_network_security_groups
+ vifs = neutron.add_ports_to_network(
+ task,
+ self.get_rescuing_network_uuid(context=task.context),
+ security_groups=security_groups)
+ for port in task.ports:
+ if port.uuid in vifs:
+ internal_info = port.internal_info
+ internal_info['rescuing_vif_port_id'] = vifs[port.uuid]
+ port.internal_info = internal_info
+ port.save()
+ return vifs
+
+ def remove_rescuing_network(self, task):
+ """Deletes neutron port created for booting the rescue ramdisk.
+
+ :param task: a TaskManager instance.
+ :raises: NetworkError
+ """
+ LOG.info('Removing rescuing network from node %s',
+ task.node.uuid)
+ neutron.remove_ports_from_network(
+ task, self.get_rescuing_network_uuid(context=task.context))
+ for port in task.ports:
+ if 'rescuing_vif_port_id' in port.internal_info:
+ internal_info = port.internal_info
+ del internal_info['rescuing_vif_port_id']
+ port.internal_info = internal_info
+ port.save()
+
def configure_tenant_networks(self, task):
"""Configure tenant networks for a node.
diff --git a/ironic/drivers/modules/network/noop.py b/ironic/drivers/modules/network/noop.py
index 5d19ecd59..5f50162ad 100644
--- a/ironic/drivers/modules/network/noop.py
+++ b/ironic/drivers/modules/network/noop.py
@@ -67,8 +67,9 @@ class NoopNetwork(base.NetworkInterface):
"""Returns the currently used VIF associated with port or portgroup
We are booting the node only in one network at a time, and presence of
- cleaning_vif_port_id means we're doing cleaning, of
- provisioning_vif_port_id - provisioning.
+ cleaning_vif_port_id means we're doing cleaning,
+ of provisioning_vif_port_id - provisioning
+ of rescuing_vif_port_id - rescuing.
Otherwise it's a tenant network
:param task: A TaskManager instance.
diff --git a/ironic/tests/base.py b/ironic/tests/base.py
index 7482d762e..b2d380bbd 100644
--- a/ironic/tests/base.py
+++ b/ironic/tests/base.py
@@ -129,6 +129,8 @@ class TestCase(oslo_test_base.BaseTestCase):
group='neutron')
self.config(provisioning_network=uuidutils.generate_uuid(),
group='neutron')
+ self.config(rescuing_network=uuidutils.generate_uuid(),
+ group='neutron')
self.config(enabled_drivers=['fake'])
self.config(enabled_hardware_types=['fake-hardware'])
self.config(enabled_network_interfaces=['flat', 'noop', 'neutron'])
diff --git a/ironic/tests/unit/common/test_network.py b/ironic/tests/unit/common/test_network.py
index bc5e26b6b..d59846385 100644
--- a/ironic/tests/unit/common/test_network.py
+++ b/ironic/tests/unit/common/test_network.py
@@ -153,6 +153,9 @@ class TestNetwork(db_base.DbTestCase):
def test_get_node_vif_ids_during_provisioning(self):
self._test_get_node_vif_ids_multitenancy('provisioning_vif_port_id')
+ def test_get_node_vif_ids_during_rescuing(self):
+ self._test_get_node_vif_ids_multitenancy('rescuing_vif_port_id')
+
class GetPortgroupByIdTestCase(db_base.DbTestCase):
diff --git a/ironic/tests/unit/drivers/modules/network/test_common.py b/ironic/tests/unit/drivers/modules/network/test_common.py
index 4fc0d513a..a64fc348f 100644
--- a/ironic/tests/unit/drivers/modules/network/test_common.py
+++ b/ironic/tests/unit/drivers/modules/network/test_common.py
@@ -626,7 +626,7 @@ class TestVifPortIDMixin(db_base.DbTestCase):
def test_get_current_vif_internal_info_provisioning(self):
internal_info = {'provisioning_vif_port_id': 'foo',
- 'vif_port_id': 'bar'}
+ 'tenant_vif_port_id': 'bar'}
self.port.internal_info = internal_info
self.port.save()
with task_manager.acquire(self.context, self.node.id) as task:
@@ -641,6 +641,15 @@ class TestVifPortIDMixin(db_base.DbTestCase):
vif = self.interface.get_current_vif(task, self.port)
self.assertEqual('bar', vif)
+ def test_get_current_vif_internal_info_rescuing(self):
+ internal_info = {'rescuing_vif_port_id': 'foo',
+ 'tenant_vif_port_id': 'bar'}
+ self.port.internal_info = internal_info
+ self.port.save()
+ with task_manager.acquire(self.context, self.node.id) as task:
+ vif = self.interface.get_current_vif(task, self.port)
+ self.assertEqual('foo', vif)
+
def test_get_current_vif_none(self):
internal_info = extra = {}
self.port.internal_info = internal_info
diff --git a/ironic/tests/unit/drivers/modules/network/test_neutron.py b/ironic/tests/unit/drivers/modules/network/test_neutron.py
index 704b6f998..df8ff531c 100644
--- a/ironic/tests/unit/drivers/modules/network/test_neutron.py
+++ b/ironic/tests/unit/drivers/modules/network/test_neutron.py
@@ -20,6 +20,7 @@ from oslo_utils import uuidutils
from ironic.common import exception
from ironic.common import neutron as neutron_common
from ironic.conductor import task_manager
+from ironic.drivers import base as drivers_base
from ironic.drivers.modules.network import neutron
from ironic.tests.unit.conductor import mgr_utils
from ironic.tests.unit.db import base as db_base
@@ -35,10 +36,19 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
def setUp(self):
super(NeutronInterfaceTestCase, self).setUp()
- self.config(enabled_drivers=['fake'])
+ self.config(enabled_hardware_types=['fake-hardware'])
+ for iface in drivers_base.ALL_INTERFACES:
+ name = 'fake'
+ if iface == 'network':
+ name = 'neutron'
+ config_kwarg = {'enabled_%s_interfaces' % iface: [name],
+ 'default_%s_interface' % iface: name}
+ self.config(**config_kwarg)
+
mgr_utils.mock_the_extension_manager()
self.interface = neutron.NeutronNetwork()
self.node = utils.create_test_node(self.context,
+ driver='fake-hardware',
network_interface='neutron')
self.port = utils.create_test_port(
self.context, node_id=self.node.id,
@@ -217,6 +227,82 @@ class NeutronInterfaceTestCase(db_base.DbTestCase):
self.port.refresh()
self.assertNotIn('cleaning_vif_port_id', self.port.internal_info)
+ @mock.patch.object(neutron_common, 'validate_network',
+ side_effect=lambda n, t, context=None: n)
+ @mock.patch.object(neutron_common, 'rollback_ports')
+ @mock.patch.object(neutron_common, 'add_ports_to_network')
+ def test_add_rescuing_network(self, add_ports_mock, rollback_mock,
+ validate_mock):
+ other_port = utils.create_test_port(
+ self.context, node_id=self.node.id,
+ address='52:54:00:cf:2d:33',
+ uuid=uuidutils.generate_uuid(),
+ extra={'vif_port_id': uuidutils.generate_uuid()})
+ neutron_other_port = {'id': uuidutils.generate_uuid(),
+ 'mac_address': '52:54:00:cf:2d:33'}
+ add_ports_mock.return_value = {
+ other_port.uuid: neutron_other_port['id']}
+ with task_manager.acquire(self.context, self.node.id) as task:
+ res = self.interface.add_rescuing_network(task)
+ add_ports_mock.assert_called_once_with(
+ task, CONF.neutron.rescuing_network,
+ security_groups=[])
+ rollback_mock.assert_called_once_with(
+ task, CONF.neutron.rescuing_network)
+ self.assertEqual(add_ports_mock.return_value, res)
+ validate_mock.assert_called_once_with(
+ CONF.neutron.rescuing_network,
+ 'rescuing network', context=task.context)
+ other_port.refresh()
+ self.assertEqual(neutron_other_port['id'],
+ other_port.internal_info['rescuing_vif_port_id'])
+ self.assertNotIn('rescuing_vif_port_id', self.port.internal_info)
+
+ @mock.patch.object(neutron_common, 'validate_network',
+ lambda n, t, context=None: n)
+ @mock.patch.object(neutron_common, 'rollback_ports')
+ @mock.patch.object(neutron_common, 'add_ports_to_network')
+ def test_add_rescuing_network_with_sg(self, add_ports_mock, rollback_mock):
+ add_ports_mock.return_value = {self.port.uuid: self.neutron_port['id']}
+ sg_ids = []
+ for i in range(2):
+ sg_ids.append(uuidutils.generate_uuid())
+ self.config(rescuing_network_security_groups=sg_ids, group='neutron')
+ with task_manager.acquire(self.context, self.node.id) as task:
+ res = self.interface.add_rescuing_network(task)
+ add_ports_mock.assert_called_once_with(
+ task, CONF.neutron.rescuing_network,
+ security_groups=CONF.neutron.rescuing_network_security_groups)
+ rollback_mock.assert_called_once_with(
+ task, CONF.neutron.rescuing_network)
+ self.assertEqual(add_ports_mock.return_value, res)
+ self.port.refresh()
+ self.assertEqual(self.neutron_port['id'],
+ self.port.internal_info['rescuing_vif_port_id'])
+
+ @mock.patch.object(neutron_common, 'validate_network',
+ side_effect=lambda n, t, context=None: n)
+ @mock.patch.object(neutron_common, 'remove_ports_from_network')
+ def test_remove_rescuing_network(self, remove_ports_mock,
+ validate_mock):
+ other_port = utils.create_test_port(
+ self.context, node_id=self.node.id,
+ address='52:54:00:cf:2d:33',
+ uuid=uuidutils.generate_uuid(),
+ extra={'vif_port_id': uuidutils.generate_uuid()})
+ other_port.internal_info = {'rescuing_vif_port_id': 'vif-port-id'}
+ other_port.save()
+ with task_manager.acquire(self.context, self.node.id) as task:
+ self.interface.remove_rescuing_network(task)
+ remove_ports_mock.assert_called_once_with(
+ task, CONF.neutron.rescuing_network)
+ validate_mock.assert_called_once_with(
+ CONF.neutron.rescuing_network,
+ 'rescuing network', context=task.context)
+ other_port.refresh()
+ self.assertNotIn('rescuing_vif_port_id', self.port.internal_info)
+ self.assertNotIn('rescuing_vif_port_id', other_port.internal_info)
+
@mock.patch.object(neutron_common, 'unbind_neutron_port')
def test_unconfigure_tenant_networks(self, mock_unbind_port):
with task_manager.acquire(self.context, self.node.id) as task: