diff --git a/etc/ironic/ironic.conf.sample b/etc/ironic/ironic.conf.sample index 3628fc1a7b..d07f276ece 100644 --- a/etc/ironic/ironic.conf.sample +++ b/etc/ironic/ironic.conf.sample @@ -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 = # 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 = # Project ID to scope to (string value) -# Deprecated group/name - [cinder]/tenant-id +# Deprecated group/name - [cinder]/tenant_id #project_id = # Project name to scope to (string value) -# Deprecated group/name - [cinder]/tenant-name +# Deprecated group/name - [cinder]/tenant_name #project_name = # 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 = # 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 = -# Timeout value for connecting to cinder in seconds. (integer -# value) -#url_timeout = 30 - # User's domain id (string value) #user_domain_id = @@ -993,7 +977,7 @@ #user_id = # Username (string value) -# Deprecated group/name - [cinder]/user-name +# Deprecated group/name - [cinder]/user_name #username = @@ -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 = # Project ID to scope to (string value) -# Deprecated group/name - [glance]/tenant-id +# Deprecated group/name - [glance]/tenant_id #project_id = # Project name to scope to (string value) -# Deprecated group/name - [glance]/tenant-name +# Deprecated group/name - [glance]/tenant_name #project_name = # The account that Glance uses to communicate with Swift. The @@ -1698,7 +1673,7 @@ #user_id = # Username (string value) -# Deprecated group/name - [glance]/user-name +# Deprecated group/name - [glance]/user_name #username = @@ -1840,11 +1815,11 @@ #project_domain_name = # Project ID to scope to (string value) -# Deprecated group/name - [inspector]/tenant-id +# Deprecated group/name - [inspector]/tenant_id #project_id = # Project name to scope to (string value) -# Deprecated group/name - [inspector]/tenant-name +# Deprecated group/name - [inspector]/tenant_name #project_name = # ironic-inspector HTTP endpoint. If this is not set, the @@ -1877,7 +1852,7 @@ #user_id = # Username (string value) -# Deprecated group/name - [inspector]/user-name +# Deprecated group/name - [inspector]/user_name #username = @@ -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 = @@ -2406,11 +2397,11 @@ #project_domain_name = # Project ID to scope to (string value) -# Deprecated group/name - [neutron]/tenant-id +# Deprecated group/name - [neutron]/tenant_id #project_id = # Project name to scope to (string value) -# Deprecated group/name - [neutron]/tenant-name +# Deprecated group/name - [neutron]/tenant_name #project_name = # Neutron network UUID or name for the ramdisk to be booted @@ -2463,7 +2454,7 @@ #user_id = # Username (string value) -# Deprecated group/name - [neutron]/user-name +# Deprecated group/name - [neutron]/user_name #username = @@ -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 = @@ -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 = # 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 = # 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 = # 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 = # Project ID to scope to (string value) -# Deprecated group/name - [service_catalog]/tenant-id +# Deprecated group/name - [service_catalog]/tenant_id #project_id = # Project name to scope to (string value) -# Deprecated group/name - [service_catalog]/tenant-name +# Deprecated group/name - [service_catalog]/tenant_name #project_name = # Tenant ID (string value) @@ -3467,7 +3414,7 @@ #user_id = # Username (string value) -# Deprecated group/name - [service_catalog]/user-name +# Deprecated group/name - [service_catalog]/user_name #username = @@ -3589,11 +3536,11 @@ #project_domain_name = # Project ID to scope to (string value) -# Deprecated group/name - [swift]/tenant-id +# Deprecated group/name - [swift]/tenant_id #project_id = # Project name to scope to (string value) -# Deprecated group/name - [swift]/tenant-name +# Deprecated group/name - [swift]/tenant_name #project_name = # Maximum number of times to retry a Swift request, before @@ -3622,5 +3569,5 @@ #user_id = # Username (string value) -# Deprecated group/name - [swift]/user-name +# Deprecated group/name - [swift]/user_name #username = diff --git a/ironic/common/cinder.py b/ironic/common/cinder.py index e4681c1e04..fcb1d71a91 100644 --- a/ironic/common/cinder.py +++ b/ironic/common/cinder.py @@ -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: diff --git a/ironic/conf/cinder.py b/ironic/conf/cinder.py index 6fb63796de..dfffa58b14 100644 --- a/ironic/conf/cinder.py +++ b/ironic/conf/cinder.py @@ -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.')), ] diff --git a/ironic/tests/unit/common/test_cinder.py b/ironic/tests/unit/common/test_cinder.py index 4310ac3f35..de1a723fb7 100644 --- a/ironic/tests/unit/common/test_cinder.py +++ b/ironic/tests/unit/common/test_cinder.py @@ -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'}) diff --git a/releasenotes/notes/add-boot-from-volume-support-9f64208f083d0691.yaml b/releasenotes/notes/add-boot-from-volume-support-9f64208f083d0691.yaml new file mode 100644 index 0000000000..766429f9d0 --- /dev/null +++ b/releasenotes/notes/add-boot-from-volume-support-9f64208f083d0691.yaml @@ -0,0 +1,4 @@ +--- +other: + - Adds a configuration section ``cinder`` and a requirement of + cinder client (python-cinderclient).