Common cinder interface additional improvements

This is a follow up to 459fe314fa

Minor issues in the code and tests are fixed, the release note is added,
configuration options are described as part of not yet exposed feature.

Change-Id: Ib466ee3970dcc1e141ddac38a54544f6a011549e
Partial-Bug: #1559691
This commit is contained in:
Joanna Taryma 2017-04-12 12:47:13 -07:00
parent 28c48ea68f
commit 3b5aefa0b6
5 changed files with 178 additions and 180 deletions

View File

@ -383,13 +383,6 @@
# Note: This option can be changed without restarting.
#debug = false
# DEPRECATED: If set to false, the logging level will be set
# to WARNING instead of the default INFO level. (boolean
# value)
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
#verbose = true
# The name of a logging configuration file. This file is
# appended to any existing logging configuration files. For
# details about logging configuration files, see the Python
@ -433,6 +426,13 @@
# ignored if log_config_append is set. (boolean value)
#use_syslog = false
# Enable journald for logging. If running in a systemd
# environment you may wish to enable journal support. Doing so
# will use the journal native protocol which includes
# structured metadata in addition to log messages.This option
# is ignored if log_config_append is set. (boolean value)
#use_journal = false
# Syslog facility to receive log lines. This option is ignored
# if log_config_append is set. (string value)
#syslog_log_facility = LOG_USER
@ -500,7 +500,6 @@
#
# Size of RPC connection pool. (integer value)
# Deprecated group/name - [DEFAULT]/rpc_conn_pool_size
#rpc_conn_pool_size = 30
# The pool size limit for connections expiration policy
@ -514,31 +513,25 @@
# ZeroMQ bind address. Should be a wildcard (*), an ethernet
# interface, or IP. The "host" option should point or resolve
# to this address. (string value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_bind_address
#rpc_zmq_bind_address = *
# MatchMaker driver. (string value)
# Allowed values: redis, sentinel, dummy
# Deprecated group/name - [DEFAULT]/rpc_zmq_matchmaker
#rpc_zmq_matchmaker = redis
# Number of ZeroMQ contexts, defaults to 1. (integer value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_contexts
#rpc_zmq_contexts = 1
# Maximum number of ingress messages to locally buffer per
# topic. Default is unlimited. (integer value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_topic_backlog
#rpc_zmq_topic_backlog = <None>
# Directory for holding IPC sockets. (string value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_ipc_dir
#rpc_zmq_ipc_dir = /var/run/openstack
# Name of this node. Must be a valid hostname, FQDN, or IP
# address. Must match "host" option, if running Nova. (string
# value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_host
#rpc_zmq_host = localhost
# Number of seconds to wait before all pending messages will
@ -554,26 +547,21 @@
# The default number of seconds that poll should wait. Poll
# raises timeout exception when timeout expired. (integer
# value)
# Deprecated group/name - [DEFAULT]/rpc_poll_timeout
#rpc_poll_timeout = 1
# Expiration timeout in seconds of a name service record about
# existing target ( < 0 means no timeout). (integer value)
# Deprecated group/name - [DEFAULT]/zmq_target_expire
#zmq_target_expire = 300
# Update period in seconds of a name service record about
# existing target. (integer value)
# Deprecated group/name - [DEFAULT]/zmq_target_update
#zmq_target_update = 180
# Use PUB/SUB pattern for fanout methods. PUB/SUB always uses
# proxy. (boolean value)
# Deprecated group/name - [DEFAULT]/use_pub_sub
#use_pub_sub = false
# Use ROUTER remote proxy. (boolean value)
# Deprecated group/name - [DEFAULT]/use_router_proxy
#use_router_proxy = false
# This option makes direct connections dynamic or static. It
@ -590,25 +578,21 @@
# Minimal port number for random ports range. (port value)
# Minimum value: 0
# Maximum value: 65535
# Deprecated group/name - [DEFAULT]/rpc_zmq_min_port
#rpc_zmq_min_port = 49153
# Maximal port number for random ports range. (integer value)
# Minimum value: 1
# Maximum value: 65536
# Deprecated group/name - [DEFAULT]/rpc_zmq_max_port
#rpc_zmq_max_port = 65536
# Number of retries to find free port number before fail with
# ZMQBindError. (integer value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_bind_port_retries
#rpc_zmq_bind_port_retries = 100
# Default serialization mechanism for
# serializing/deserializing outgoing/incoming messages (string
# value)
# Allowed values: json, msgpack
# Deprecated group/name - [DEFAULT]/rpc_zmq_serialization
#rpc_zmq_serialization = json
# This option configures round-robin mode in zmq socket. True
@ -684,7 +668,8 @@
# taken from the matchmaker. (list value)
#subscribe_on =
# Size of executor thread pool. (integer value)
# Size of executor thread pool when executor is threading or
# eventlet. (integer value)
# Deprecated group/name - [DEFAULT]/rpc_thread_pool_size
#executor_thread_pool_size = 64
@ -952,15 +937,16 @@
#project_domain_name = <None>
# Project ID to scope to (string value)
# Deprecated group/name - [cinder]/tenant-id
# Deprecated group/name - [cinder]/tenant_id
#project_id = <None>
# Project name to scope to (string value)
# Deprecated group/name - [cinder]/tenant-name
# Deprecated group/name - [cinder]/tenant_name
#project_name = <None>
# Client retries in the case of a failed request connection.
# (integer value)
# This option is part of boot-from-volume work, which is not
# currently exposed to users. (integer value)
#retries = 3
# Tenant ID (string value)
@ -976,13 +962,11 @@
#trust_id = <None>
# URL for connecting to cinder. If set, the value must start
# with either http:// or https://. (string value)
# with either http:// or https://. This option is part of
# boot-from-volume work, which is not currently exposed to
# users. (string value)
#url = <None>
# Timeout value for connecting to cinder in seconds. (integer
# value)
#url_timeout = 30
# User's domain id (string value)
#user_domain_id = <None>
@ -993,7 +977,7 @@
#user_id = <None>
# Username (string value)
# Deprecated group/name - [cinder]/user-name
# Deprecated group/name - [cinder]/user_name
#username = <None>
@ -1251,16 +1235,7 @@
# From oslo.db
#
# DEPRECATED: The file name to use with SQLite. (string value)
# Deprecated group/name - [DEFAULT]/sqlite_db
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Should use config option connection or
# slave_connection to connect the database.
#sqlite_db = oslo.sqlite
# If True, SQLite uses synchronous mode. (boolean value)
# Deprecated group/name - [DEFAULT]/sqlite_synchronous
#sqlite_synchronous = true
# The back end to use for the database. (string value)
@ -1591,11 +1566,11 @@
#project_domain_name = <None>
# Project ID to scope to (string value)
# Deprecated group/name - [glance]/tenant-id
# Deprecated group/name - [glance]/tenant_id
#project_id = <None>
# Project name to scope to (string value)
# Deprecated group/name - [glance]/tenant-name
# Deprecated group/name - [glance]/tenant_name
#project_name = <None>
# The account that Glance uses to communicate with Swift. The
@ -1698,7 +1673,7 @@
#user_id = <None>
# Username (string value)
# Deprecated group/name - [glance]/user-name
# Deprecated group/name - [glance]/user_name
#username = <None>
@ -1840,11 +1815,11 @@
#project_domain_name = <None>
# Project ID to scope to (string value)
# Deprecated group/name - [inspector]/tenant-id
# Deprecated group/name - [inspector]/tenant_id
#project_id = <None>
# Project name to scope to (string value)
# Deprecated group/name - [inspector]/tenant-name
# Deprecated group/name - [inspector]/tenant_name
#project_name = <None>
# ironic-inspector HTTP endpoint. If this is not set, the
@ -1877,7 +1852,7 @@
#user_id = <None>
# Username (string value)
# Deprecated group/name - [inspector]/user-name
# Deprecated group/name - [inspector]/user_name
#username = <None>
@ -2178,6 +2153,22 @@
# Reason: PKI token format is no longer supported.
#hash_algorithms = md5
# A choice of roles that must be present in a service token.
# Service tokens are allowed to request that an expired token
# can be used and so this check should tightly control that
# only actual services should be sending this token. Roles
# here are applied as an ANY check so any role in this list
# must be present. For backwards compatibility reasons this
# currently only affects the allow_expired check. (list value)
#service_token_roles = service
# For backwards compatibility reasons we must let valid
# service tokens pass that don't pass the service_token_roles
# check as valid. Setting this true will become the default in
# a future release and should be enabled if possible. (boolean
# value)
#service_token_roles_required = false
# Authentication type to load (string value)
# Deprecated group/name - [keystone_authtoken]/auth_plugin
#auth_type = <None>
@ -2406,11 +2397,11 @@
#project_domain_name = <None>
# Project ID to scope to (string value)
# Deprecated group/name - [neutron]/tenant-id
# Deprecated group/name - [neutron]/tenant_id
#project_id = <None>
# Project name to scope to (string value)
# Deprecated group/name - [neutron]/tenant-name
# Deprecated group/name - [neutron]/tenant_name
#project_name = <None>
# Neutron network UUID or name for the ramdisk to be booted
@ -2463,7 +2454,7 @@
#user_id = <None>
# Username (string value)
# Deprecated group/name - [neutron]/user-name
# Deprecated group/name - [neutron]/user_name
#username = <None>
@ -2512,7 +2503,6 @@
#
# Enables or disables inter-process locks. (boolean value)
# Deprecated group/name - [DEFAULT]/disable_process_locking
#disable_process_locking = false
# Directory to use for lock files. For security, the
@ -2520,7 +2510,6 @@
# running the processes that need locking. Defaults to
# environment variable OSLO_LOCK_PATH. If external locks are
# used, a lock path must be set. (string value)
# Deprecated group/name - [DEFAULT]/lock_path
#lock_path = <None>
@ -2532,41 +2521,38 @@
# Name for the AMQP container. must be globally unique.
# Defaults to a generated UUID (string value)
# Deprecated group/name - [amqp1]/container_name
#container_name = <None>
# Timeout for inactive connections (in seconds) (integer
# value)
# Deprecated group/name - [amqp1]/idle_timeout
#idle_timeout = 0
# Debug: dump AMQP frames to stdout (boolean value)
# Deprecated group/name - [amqp1]/trace
#trace = false
# Attempt to connect via SSL. If no other ssl-related
# parameters are given, it will use the system's CA-bundle to
# verify the server's certificate. (boolean value)
#ssl = false
# CA certificate PEM file used to verify the server's
# certificate (string value)
# Deprecated group/name - [amqp1]/ssl_ca_file
#ssl_ca_file =
# Self-identifying certificate PEM file for client
# authentication (string value)
# Deprecated group/name - [amqp1]/ssl_cert_file
#ssl_cert_file =
# Private key PEM file used to sign ssl_cert_file certificate
# (optional) (string value)
# Deprecated group/name - [amqp1]/ssl_key_file
#ssl_key_file =
# Password for decrypting ssl_key_file (if encrypted) (string
# value)
# Deprecated group/name - [amqp1]/ssl_key_password
#ssl_key_password = <None>
# DEPRECATED: Accept clients using either SSL or plain TCP
# (boolean value)
# Deprecated group/name - [amqp1]/allow_insecure_clients
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Not applicable - not a SSL server
@ -2574,25 +2560,20 @@
# Space separated list of acceptable SASL mechanisms (string
# value)
# Deprecated group/name - [amqp1]/sasl_mechanisms
#sasl_mechanisms =
# Path to directory that contains the SASL configuration
# (string value)
# Deprecated group/name - [amqp1]/sasl_config_dir
#sasl_config_dir =
# Name of configuration file (without .conf suffix) (string
# value)
# Deprecated group/name - [amqp1]/sasl_config_name
#sasl_config_name =
# User name for message broker authentication (string value)
# Deprecated group/name - [amqp1]/username
#username =
# Password for message broker authentication (string value)
# Deprecated group/name - [amqp1]/password
#password =
# Seconds to pause before attempting to re-connect. (integer
@ -2653,17 +2634,14 @@
# address prefix used when sending to a specific server
# (string value)
# Deprecated group/name - [amqp1]/server_request_prefix
#server_request_prefix = exclusive
# address prefix used when broadcasting to all servers (string
# value)
# Deprecated group/name - [amqp1]/broadcast_prefix
#broadcast_prefix = broadcast
# address prefix when sending to any server in group (string
# value)
# Deprecated group/name - [amqp1]/group_request_prefix
#group_request_prefix = unicast
# Address prefix for all generated RPC addresses (string
@ -2820,7 +2798,6 @@
#amqp_durable_queues = false
# Auto-delete queues in AMQP. (boolean value)
# Deprecated group/name - [DEFAULT]/amqp_auto_delete
#amqp_auto_delete = false
# Enable SSL (boolean value)
@ -2847,7 +2824,6 @@
# How long to wait before reconnecting in response to an AMQP
# consumer cancel notification. (floating point value)
# Deprecated group/name - [DEFAULT]/kombu_reconnect_delay
#kombu_reconnect_delay = 1.0
# EXPERIMENTAL: Possible values are: gzip, bz2. If not set
@ -2870,7 +2846,6 @@
# DEPRECATED: The RabbitMQ broker address where a single node
# is used. (string value)
# Deprecated group/name - [DEFAULT]/rabbit_host
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Replaced by [DEFAULT]/transport_url
@ -2880,7 +2855,6 @@
# used. (port value)
# Minimum value: 0
# Maximum value: 65535
# Deprecated group/name - [DEFAULT]/rabbit_port
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Replaced by [DEFAULT]/transport_url
@ -2888,21 +2862,18 @@
# DEPRECATED: RabbitMQ HA cluster host:port pairs. (list
# value)
# Deprecated group/name - [DEFAULT]/rabbit_hosts
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Replaced by [DEFAULT]/transport_url
#rabbit_hosts = $rabbit_host:$rabbit_port
# DEPRECATED: The RabbitMQ userid. (string value)
# Deprecated group/name - [DEFAULT]/rabbit_userid
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Replaced by [DEFAULT]/transport_url
#rabbit_userid = guest
# DEPRECATED: The RabbitMQ password. (string value)
# Deprecated group/name - [DEFAULT]/rabbit_password
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Replaced by [DEFAULT]/transport_url
@ -2910,11 +2881,9 @@
# The RabbitMQ login method. (string value)
# Allowed values: PLAIN, AMQPLAIN, RABBIT-CR-DEMO
# Deprecated group/name - [DEFAULT]/rabbit_login_method
#rabbit_login_method = AMQPLAIN
# DEPRECATED: The RabbitMQ virtual host. (string value)
# Deprecated group/name - [DEFAULT]/rabbit_virtual_host
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
# Reason: Replaced by [DEFAULT]/transport_url
@ -2926,7 +2895,6 @@
# How long to backoff for between retries when connecting to
# RabbitMQ. (integer value)
# Deprecated group/name - [DEFAULT]/rabbit_retry_backoff
#rabbit_retry_backoff = 2
# Maximum interval of RabbitMQ connection retries. Default is
@ -2935,7 +2903,6 @@
# DEPRECATED: Maximum number of RabbitMQ connection retries.
# Default is 0 (infinite retry count). (integer value)
# Deprecated group/name - [DEFAULT]/rabbit_max_retries
# This option is deprecated for removal.
# Its value may be silently ignored in the future.
#rabbit_max_retries = 0
@ -2948,7 +2915,6 @@
# generated names) are mirrored across all nodes, run:
# "rabbitmqctl set_policy HA '^(?!amq\.).*' '{"ha-mode":
# "all"}' " (boolean value)
# Deprecated group/name - [DEFAULT]/rabbit_ha_queues
#rabbit_ha_queues = false
# Positive integer representing duration in seconds for queue
@ -2973,7 +2939,6 @@
# Deprecated, use rpc_backend=kombu+memory or rpc_backend=fake
# (boolean value)
# Deprecated group/name - [DEFAULT]/fake_rabbit
#fake_rabbit = false
# Maximum number of channels to allow (integer value)
@ -3099,31 +3064,25 @@
# ZeroMQ bind address. Should be a wildcard (*), an ethernet
# interface, or IP. The "host" option should point or resolve
# to this address. (string value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_bind_address
#rpc_zmq_bind_address = *
# MatchMaker driver. (string value)
# Allowed values: redis, sentinel, dummy
# Deprecated group/name - [DEFAULT]/rpc_zmq_matchmaker
#rpc_zmq_matchmaker = redis
# Number of ZeroMQ contexts, defaults to 1. (integer value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_contexts
#rpc_zmq_contexts = 1
# Maximum number of ingress messages to locally buffer per
# topic. Default is unlimited. (integer value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_topic_backlog
#rpc_zmq_topic_backlog = <None>
# Directory for holding IPC sockets. (string value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_ipc_dir
#rpc_zmq_ipc_dir = /var/run/openstack
# Name of this node. Must be a valid hostname, FQDN, or IP
# address. Must match "host" option, if running Nova. (string
# value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_host
#rpc_zmq_host = localhost
# Number of seconds to wait before all pending messages will
@ -3139,26 +3098,21 @@
# The default number of seconds that poll should wait. Poll
# raises timeout exception when timeout expired. (integer
# value)
# Deprecated group/name - [DEFAULT]/rpc_poll_timeout
#rpc_poll_timeout = 1
# Expiration timeout in seconds of a name service record about
# existing target ( < 0 means no timeout). (integer value)
# Deprecated group/name - [DEFAULT]/zmq_target_expire
#zmq_target_expire = 300
# Update period in seconds of a name service record about
# existing target. (integer value)
# Deprecated group/name - [DEFAULT]/zmq_target_update
#zmq_target_update = 180
# Use PUB/SUB pattern for fanout methods. PUB/SUB always uses
# proxy. (boolean value)
# Deprecated group/name - [DEFAULT]/use_pub_sub
#use_pub_sub = false
# Use ROUTER remote proxy. (boolean value)
# Deprecated group/name - [DEFAULT]/use_router_proxy
#use_router_proxy = false
# This option makes direct connections dynamic or static. It
@ -3175,25 +3129,21 @@
# Minimal port number for random ports range. (port value)
# Minimum value: 0
# Maximum value: 65535
# Deprecated group/name - [DEFAULT]/rpc_zmq_min_port
#rpc_zmq_min_port = 49153
# Maximal port number for random ports range. (integer value)
# Minimum value: 1
# Maximum value: 65536
# Deprecated group/name - [DEFAULT]/rpc_zmq_max_port
#rpc_zmq_max_port = 65536
# Number of retries to find free port number before fail with
# ZMQBindError. (integer value)
# Deprecated group/name - [DEFAULT]/rpc_zmq_bind_port_retries
#rpc_zmq_bind_port_retries = 100
# Default serialization mechanism for
# serializing/deserializing outgoing/incoming messages (string
# value)
# Allowed values: json, msgpack
# Deprecated group/name - [DEFAULT]/rpc_zmq_serialization
#rpc_zmq_serialization = json
# This option configures round-robin mode in zmq socket. True
@ -3277,12 +3227,10 @@
#
# The file that defines policies. (string value)
# Deprecated group/name - [DEFAULT]/policy_file
#policy_file = policy.json
# Default rule. Enforced when a requested rule is not found.
# (string value)
# Deprecated group/name - [DEFAULT]/policy_default_rule
#policy_default_rule = default
# Directories where policy configuration files are stored.
@ -3291,7 +3239,6 @@
# file defined by policy_file must exist for these directories
# to be searched. Missing or empty directories are ignored.
# (multi valued)
# Deprecated group/name - [DEFAULT]/policy_dirs
#policy_dirs = policy.d
@ -3438,11 +3385,11 @@
#project_domain_name = <None>
# Project ID to scope to (string value)
# Deprecated group/name - [service_catalog]/tenant-id
# Deprecated group/name - [service_catalog]/tenant_id
#project_id = <None>
# Project name to scope to (string value)
# Deprecated group/name - [service_catalog]/tenant-name
# Deprecated group/name - [service_catalog]/tenant_name
#project_name = <None>
# Tenant ID (string value)
@ -3467,7 +3414,7 @@
#user_id = <None>
# Username (string value)
# Deprecated group/name - [service_catalog]/user-name
# Deprecated group/name - [service_catalog]/user_name
#username = <None>
@ -3589,11 +3536,11 @@
#project_domain_name = <None>
# Project ID to scope to (string value)
# Deprecated group/name - [swift]/tenant-id
# Deprecated group/name - [swift]/tenant_id
#project_id = <None>
# Project name to scope to (string value)
# Deprecated group/name - [swift]/tenant-name
# Deprecated group/name - [swift]/tenant_name
#project_name = <None>
# Maximum number of times to retry a Swift request, before
@ -3622,5 +3569,5 @@
#user_id = <None>
# Username (string value)
# Deprecated group/name - [swift]/user-name
# Deprecated group/name - [swift]/user_name
#username = <None>

View File

@ -104,14 +104,15 @@ def _get_attachment_id(node, volume):
# attachment for each node that represents all possible attachment
# information as multiple types can be submitted in a single request.
attachments = volume.attachments
if attachments is not None:
for attachment in attachments:
if attachment.get('server_id') in (node.instance_uuid, node.uuid):
return attachment.get('attachment_id')
if attachments is None:
return
for attachment in attachments:
if attachment.get('server_id') in (node.instance_uuid, node.uuid):
return attachment.get('attachment_id')
def _create_metadata_dictionary(node, action):
"""Create a volume metadata dictionary utilizing the node UUID.
"""Create a volume metadata dictionary.
:param node: Object representing a node.
:param action: String value representing the last action.
@ -121,12 +122,12 @@ def _create_metadata_dictionary(node, action):
label = "ironic_node_%s" % node.uuid
return {
label: {
'instance_uuid': node.instance_uuid,
'instance_uuid': node.instance_uuid or node.uuid,
'last_seen': datetime.datetime.utcnow().isoformat(),
'last_action': action}}
def _init_client_for_operations(task):
def _init_client(task):
"""Obtain cinder client and return it for use.
:param task: TaskManager instance representing the operation.
@ -138,8 +139,8 @@ def _init_client_for_operations(task):
try:
return get_client()
except Exception as e:
msg = (_('Failed to initialize cinder client for node %(uuid)s: %('
'err)s') % {'uuid': node.uuid, 'err': e})
msg = (_('Failed to initialize cinder client for operations on node '
'%(uuid)s: %(err)s') % {'uuid': node.uuid, 'err': e})
LOG.error(msg)
raise exception.StorageError(msg)
@ -181,7 +182,6 @@ def attach_volumes(task, volume_list, connector):
}
:raises: StorageError If storage subsystem exception is raised.
:raises: TypeError If the supplied volume_list is not a list.
:returns: List of connected volumes, including volumes that were
already connected to desired nodes. The returned list
can be relatively consistent depending on the end storage
@ -232,7 +232,7 @@ def attach_volumes(task, volume_list, connector):
}]
"""
node = task.node
client = _init_client_for_operations(task)
client = _init_client(task)
connected = []
for volume_id in volume_list:
@ -271,10 +271,6 @@ def attach_volumes(task, volume_list, connector):
# Provide connector information to cinder
connection = client.volumes.initialize_connection(volume_id,
connector)
if 'volume_id' not in connection['data']:
connection['data']['volume_id'] = volume_id
connection['data']['ironic_volume_uuid'] = volume.uuid
connected.append(connection)
except cinder_exceptions.ClientException as e:
msg = (_('Failed to initialize connection for volume '
'%(vol_id)s to node %(node)s: %(err)s') %
@ -282,6 +278,11 @@ def attach_volumes(task, volume_list, connector):
LOG.error(msg)
raise exception.StorageError(msg)
if 'volume_id' not in connection['data']:
connection['data']['volume_id'] = volume_id
connection['data']['ironic_volume_uuid'] = volume.uuid
connected.append(connection)
LOG.info('Successfully initialized volume %(vol_id)s for '
'node %(node)s.', {'vol_id': volume_id, 'node': node.uuid})
@ -351,7 +352,6 @@ def detach_volumes(task, volume_list, connector, allow_errors=False):
:param allow_errors: Boolean value governing if errors that are returned
are treated as warnings instead of exceptions.
Default False.
:raises: TypeError If the supplied volume_list is not a sequence.
:raises: StorageError
"""
def _handle_errors(msg):
@ -361,7 +361,7 @@ def detach_volumes(task, volume_list, connector, allow_errors=False):
LOG.error(msg)
raise exception.StorageError(msg)
client = _init_client_for_operations(task)
client = _init_client(task)
node = task.node
for volume_id in volume_list:

View File

@ -20,11 +20,14 @@ opts = [
cfg.StrOpt('url',
regex='^http(s?):\/\/.+',
help=_('URL for connecting to cinder. If set, the value must '
'start with either http:// or https://.')),
'start with either http:// or https://. This option is '
'part of boot-from-volume work, which is not currently '
'exposed to users.')),
cfg.IntOpt('retries',
default=3,
help=_('Client retries in the case of a failed request '
'connection.')),
'connection. This option is part of boot-from-volume '
'work, which is not currently exposed to users.')),
]

View File

@ -11,12 +11,11 @@
# License for the specific language governing permissions and limitations
# under the License.
import mock
from cinderclient import exceptions as cinder_exceptions
import cinderclient.v3 as cinderclient
import mock
from oslo_utils import uuidutils
from six.moves import http_client
from ironic.common import cinder
@ -108,10 +107,6 @@ class TestCinderUtils(db_base.DbTestCase):
self.context,
instance_uuid=uuidutils.generate_uuid())
def test_cinder_states(self):
self.assertEqual('available', cinder.AVAILABLE)
self.assertEqual('in-use', cinder.IN_USE)
def test_is_volume_available(self):
available_volumes = [
mock.Mock(status=cinder.AVAILABLE, multiattach=False),
@ -122,13 +117,13 @@ class TestCinderUtils(db_base.DbTestCase):
for vol in available_volumes:
result = cinder.is_volume_available(vol)
self.assertEqual(True, result,
message="Failed for status '%s'." % vol.status)
self.assertTrue(result,
msg="Failed for status '%s'." % vol.status)
for vol in unavailable_volumes:
result = cinder.is_volume_available(vol)
self.assertEqual(False, result,
message="Failed for status '%s'." % vol.status)
self.assertFalse(result,
msg="Failed for status '%s'." % vol.status)
def test_is_volume_attached(self):
attached_vol = mock.Mock(id='foo', attachments=[
@ -168,12 +163,9 @@ class TestCinderUtils(db_base.DbTestCase):
}
result = cinder._create_metadata_dictionary(self.node, 'meow')
self.maxDiff = None
# Since datetime is an internal, we can't exactly mock it.
# We can however verify it's presence, and replace it.
self.assertIsInstance(result[expected_key]['last_seen'], str)
result[expected_key]['last_seen'] = 'faked-time'
self.assertDictEqual(expected, result)
self.assertEqual(expected, result)
@mock.patch.object(cinder, '_get_cinder_session', autospec=True)
@ -229,7 +221,7 @@ class TestCinderActions(db_base.DbTestCase):
with task_manager.acquire(self.context, self.node.uuid) as task:
attachments = cinder.attach_volumes(task, volumes, connector)
self.assertDictEqual(expected[0], attachments[0])
self.assertEqual(expected, attachments)
mock_reserve.assert_called_once_with(mock.ANY, volume_id)
mock_init.assert_called_once_with(mock.ANY, volume_id, connector)
mock_attach.assert_called_once_with(mock.ANY, volume_id,
@ -292,12 +284,13 @@ class TestCinderActions(db_base.DbTestCase):
mock_set_meta.assert_called_once_with(mock.ANY, volume_id,
{'bar': 'baz'})
@mock.patch.object(cinderclient.Client, '__init__')
@mock.patch.object(cinderclient.Client, '__init__', autospec=True)
def test_attach_volumes_client_init_failure(
self, mock_client, mock_get, mock_set_meta, mock_session):
connector = {'foo': 'bar'}
volumes = ['111111111-0000-0000-0000-000000000003']
mock_client.side_effect = cinder_exceptions.BadRequest(400)
mock_client.side_effect = cinder_exceptions.BadRequest(
http_client.BAD_REQUEST)
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.StorageError,
@ -318,9 +311,10 @@ class TestCinderActions(db_base.DbTestCase):
mock_get, mock_set_meta, mock_session):
"""Raise an error if the volume lookup fails"""
def __mock_get_side_effect(*args, **kwargs):
if args[1] == 'not_found':
raise cinder_exceptions.NotFound(404, message='error')
def __mock_get_side_effect(client, volume_id):
if volume_id == 'not_found':
raise cinder_exceptions.NotFound(
http_client.NOT_FOUND, message='error')
else:
return mock.Mock(attachments=[], uuid='000-000')
@ -362,7 +356,8 @@ class TestCinderActions(db_base.DbTestCase):
volume = mock.Mock(attachments=[])
mock_get.return_value = volume
mock_is_attached.return_value = False
mock_reserve.side_effect = cinder_exceptions.NotAcceptable(406)
mock_reserve.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.StorageError,
@ -389,7 +384,8 @@ class TestCinderActions(db_base.DbTestCase):
mock_create_meta.return_value = {'bar': 'baz'}
mock_is_attached.return_value = False
mock_get.return_value = mock.Mock(attachments=[])
mock_init.side_effect = cinder_exceptions.NotAcceptable(406)
mock_init.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.StorageError,
@ -426,8 +422,8 @@ class TestCinderActions(db_base.DbTestCase):
'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002',
'target_portal': '127.0.0.0.1:3260',
'target_lun': 2}}
mock_attach.side_effect = cinder_exceptions.ClientException(406,
'error')
mock_attach.side_effect = cinder_exceptions.ClientException(
http_client.NOT_ACCEPTABLE, 'error')
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.StorageError, cinder.attach_volumes,
@ -476,12 +472,13 @@ class TestCinderActions(db_base.DbTestCase):
'target_iqn': 'iqn.2010-10.org.openstack:volume-00000002',
'target_portal': '127.0.0.0.1:3260',
'target_lun': 2}}
mock_set_meta.side_effect = cinder_exceptions.NotAcceptable(406)
mock_set_meta.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
with task_manager.acquire(self.context, self.node.uuid) as task:
attachments = cinder.attach_volumes(task, volumes, connector)
self.assertDictEqual(expected[0], attachments[0])
self.assertEqual(expected, attachments)
mock_reserve.assert_called_once_with(mock.ANY, volume_id)
mock_init.assert_called_once_with(mock.ANY, volume_id, connector)
mock_attach.assert_called_once_with(mock.ANY, volume_id,
@ -556,22 +553,28 @@ class TestCinderActions(db_base.DbTestCase):
{'bar': 'baz'})
@mock.patch.object(cinderclient.Client, '__init__', autospec=True)
def test_detach_volumes_client_init_failure(
def test_detach_volumes_client_init_failure_bad_request(
self, mock_client, mock_get, mock_set_meta, mock_session):
connector = {'foo': 'bar'}
volumes = ['111111111-0000-0000-0000-000000000003']
with task_manager.acquire(self.context, self.node.uuid) as task:
mock_client.side_effect = cinder_exceptions.BadRequest(400)
mock_client.side_effect = cinder_exceptions.BadRequest(
http_client.BAD_REQUEST)
self.assertRaises(exception.StorageError,
cinder.detach_volumes,
task,
volumes,
connector)
# While we would be permitting failures, this is an
# exception that must be raised since the client
# cannot be initialized.
@mock.patch.object(cinderclient.Client, '__init__', autospec=True)
def test_detach_volumes_client_init_failure_invalid_parameter_value(
self, mock_client, mock_get, mock_set_meta, mock_session):
connector = {'foo': 'bar'}
volumes = ['111111111-0000-0000-0000-000000000003']
with task_manager.acquire(self.context, self.node.uuid) as task:
# While we would be permitting failures, this is an exception that
# must be raised since the client cannot be initialized.
mock_client.side_effect = exception.InvalidParameterValue('error')
self.assertRaises(exception.StorageError,
cinder.detach_volumes, task, volumes,
@ -583,7 +586,7 @@ class TestCinderActions(db_base.DbTestCase):
volumes = ['vol1']
connector = {'foo': 'bar'}
mock_get.side_effect = cinder_exceptions.NotFound(
404, message='error')
http_client.NOT_FOUND, message='error')
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.StorageError,
@ -616,7 +619,8 @@ class TestCinderActions(db_base.DbTestCase):
mock_get.return_value = volume
mock_create_meta.return_value = {'bar': 'baz'}
mock_is_attached.return_value = True
mock_begin.side_effect = cinder_exceptions.NotAcceptable(406)
mock_begin.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.StorageError,
@ -648,7 +652,8 @@ class TestCinderActions(db_base.DbTestCase):
mock_create_meta.return_value = {'bar': 'baz'}
mock_is_attached.return_value = True
mock_get.return_value = {'id': volume_id, 'attachments': []}
mock_term.side_effect = cinder_exceptions.NotAcceptable(406)
mock_term.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
with task_manager.acquire(self.context, self.node.uuid) as task:
self.assertRaises(exception.StorageError,
@ -669,38 +674,21 @@ class TestCinderActions(db_base.DbTestCase):
autospec=True)
@mock.patch.object(cinder, 'is_volume_attached', autospec=True)
@mock.patch.object(cinder, '_create_metadata_dictionary', autospec=True)
def test_detach_volumes_detach_meta_failure(
def test_detach_volumes_detach_failure_errors_not_allowed(
self, mock_create_meta, mock_is_attached, mock_begin, mock_term,
mock_detach, mock_get, mock_set_meta, mock_session):
volume_id = '111111111-0000-0000-0000-000000000003'
volumes = [volume_id]
connector = {'foo': 'bar'}
mock_create_meta.return_value = {'bar': 'baz'}
mock_is_attached.return_value = True
mock_get.return_value = mock.Mock(attachments=[
{'server_id': self.node.uuid, 'attachment_id': 'qux'}])
mock_detach.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
with task_manager.acquire(self.context, self.node.uuid) as task:
mock_detach.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
cinder.detach_volumes(task, volumes, connector, allow_errors=True)
mock_detach.assert_called_once_with(mock.ANY, volume_id, 'qux')
mock_set_meta.assert_called_once_with(mock.ANY, volume_id,
{'bar': 'baz'})
mock_detach.reset_mock()
mock_set_meta.reset_mock()
mock_set_meta.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
cinder.detach_volumes(task, volumes, connector, allow_errors=True)
mock_detach.assert_called_once_with(mock.ANY, volume_id, 'qux')
mock_set_meta.assert_called_once_with(mock.ANY, volume_id,
{'bar': 'baz'})
mock_detach.reset_mock()
mock_set_meta.reset_mock()
self.assertRaises(exception.StorageError,
cinder.detach_volumes,
task,
@ -709,3 +697,59 @@ class TestCinderActions(db_base.DbTestCase):
allow_errors=False)
mock_detach.assert_called_once_with(mock.ANY, volume_id, 'qux')
self.assertFalse(mock_set_meta.called)
@mock.patch.object(cinderclient.volumes.VolumeManager, 'detach',
autospec=True)
@mock.patch.object(cinderclient.volumes.VolumeManager,
'terminate_connection', autospec=True)
@mock.patch.object(cinderclient.volumes.VolumeManager, 'begin_detaching',
autospec=True)
@mock.patch.object(cinder, 'is_volume_attached', autospec=True)
@mock.patch.object(cinder, '_create_metadata_dictionary', autospec=True)
def test_detach_volumes_detach_failure_errors_allowed(
self, mock_create_meta, mock_is_attached, mock_begin, mock_term,
mock_detach, mock_get, mock_set_meta, mock_session):
volume_id = '111111111-0000-0000-0000-000000000003'
volumes = [volume_id]
connector = {'foo': 'bar'}
mock_create_meta.return_value = {'bar': 'baz'}
mock_is_attached.return_value = True
mock_get.return_value = mock.Mock(attachments=[
{'server_id': self.node.uuid, 'attachment_id': 'qux'}])
mock_set_meta.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
with task_manager.acquire(self.context, self.node.uuid) as task:
cinder.detach_volumes(task, volumes, connector, allow_errors=True)
mock_detach.assert_called_once_with(mock.ANY, volume_id, 'qux')
mock_set_meta.assert_called_once_with(mock.ANY, volume_id,
{'bar': 'baz'})
@mock.patch.object(cinderclient.volumes.VolumeManager, 'detach',
autospec=True)
@mock.patch.object(cinderclient.volumes.VolumeManager,
'terminate_connection', autospec=True)
@mock.patch.object(cinderclient.volumes.VolumeManager, 'begin_detaching',
autospec=True)
@mock.patch.object(cinder, 'is_volume_attached', autospec=True)
@mock.patch.object(cinder, '_create_metadata_dictionary', autospec=True)
def test_detach_volumes_detach_meta_failure_errors_not_allowed(
self, mock_create_meta, mock_is_attached, mock_begin, mock_term,
mock_detach, mock_get, mock_set_meta, mock_session):
volume_id = '111111111-0000-0000-0000-000000000003'
volumes = [volume_id]
connector = {'foo': 'bar'}
mock_create_meta.return_value = {'bar': 'baz'}
mock_is_attached.return_value = True
mock_get.return_value = mock.Mock(attachments=[
{'server_id': self.node.uuid, 'attachment_id': 'qux'}])
mock_set_meta.side_effect = cinder_exceptions.NotAcceptable(
http_client.NOT_ACCEPTABLE)
with task_manager.acquire(self.context, self.node.uuid) as task:
cinder.detach_volumes(task, volumes, connector, allow_errors=False)
mock_detach.assert_called_once_with(mock.ANY, volume_id, 'qux')
mock_set_meta.assert_called_once_with(mock.ANY, volume_id,
{'bar': 'baz'})

View File

@ -0,0 +1,4 @@
---
other:
- Adds a configuration section ``cinder`` and a requirement of
cinder client (python-cinderclient).