From: John Griffith Date: Fri, 9 Oct 2015 23:31:15 +0000 (-0600) Subject: Update config format for replication_devices X-Git-Url: https://review.fuel-infra.org/gitweb?a=commitdiff_plain;h=5ee2efe2234f2150bf92fc424dcfcb70ea563445;p=openstack-build%2Fcinder-build.git Update config format for replication_devices The first pass at setting up config entries for replication targets was a hacky custom string parser with its own unique syntax. A better option is to use oslo.cfg's MultiOpt in conjunction with Dict types so we can eliminate quite a bit of custom parsing and most importantly use a standard syntax for conf entries to make things easy for admins and reduce the probability of syntax errors and mistakes in config setttings. Change-Id: Ie300c1f1db548d258906eebbcea8265583086468 Closes-Bug: #1504696 --- diff --git a/cinder/volume/driver.py b/cinder/volume/driver.py index 2eba2cca3..b9faae9d1 100644 --- a/cinder/volume/driver.py +++ b/cinder/volume/driver.py @@ -20,6 +20,7 @@ import time from oslo_concurrency import processutils from oslo_config import cfg +from oslo_config import types from oslo_log import log as logging from oslo_utils import excutils import six @@ -217,20 +218,17 @@ volume_opts = [ help='List of options that control which trace info ' 'is written to the DEBUG log level to assist ' 'developers. Valid values are method and api.'), - cfg.BoolOpt('managed_replication_target', - default=True, - help='There are two types of target configurations ' - 'managed (replicate to another configured backend) ' - 'or unmanaged (replicate to a device not managed ' - 'by Cinder).'), - cfg.ListOpt('replication_devices', - default=None, - help="List of k/v pairs representing a replication target " - "for this backend device. For unmanaged the format " - "is: {'key-1'='val1' 'key-2'='val2'...},{...} " - "and for managed devices its simply a list of valid " - "configured backend_names that the driver supports " - "replicating to: backend-a,bakcend-b..."), + cfg.MultiOpt('replication_device', + item_type=types.Dict(), + default=None, + help="Multi opt of dictionaries to represent a replication " + "target device. This option may be specified multiple " + "times in a single config section to specify multiple " + "replication target devices. Each entry takes the " + "standard dict config form: replication_device = " + "device_target_id:," + "managed_backend_name:," + "key1:value1,key2:value2..."), cfg.BoolOpt('image_upload_use_cinder_backend', default=False, help='If set to True, upload-to-image in raw format will ' diff --git a/doc/source/devref/replication.rst b/doc/source/devref/replication.rst index efccd1571..46a759599 100644 --- a/doc/source/devref/replication.rst +++ b/doc/source/devref/replication.rst @@ -16,26 +16,27 @@ Config file examples -------------------- The cinder.conf file is used to specify replication target -devices for a specific driver. There are two types of target -devices that can be configured: +devices for a specific driver. Replication targets may +be specified as external (unmanaged) or internally +Cinder managed backend devices. - 1. Cinder Managed (represented by the volume-backend name) - 2. External devices (require vendor specific data to configure) +**replication_device** -NOTE that it is expected to be an error to have both managed and unmanaged replication -config variables set for a single driver. +Is a multi-dict opt, that should be specified +for each replication target device the admin would +like to configure. -Cinder managed target device ------------------------------ +*NOTE:* -In the case of a Cinder managed target device, we simply -use another Cinder configured backend as the replication -target. +There are two standardized keys in the config +entry, all others are vendor-unique: -For example if we have two backend devices foo and biz that -can replicate to each other, we can set up backend biz as -a replication target for device foo using the following -config entries:: +* device_target_id: +* managed_backend_name:," + + +An example config entry for a managed replication device +would look like this:: ..... [driver-biz] @@ -45,42 +46,98 @@ config entries:: [driver-foo] volume_driver=xxxx volume_backend_name=foo - managed_replication_target=True - replication_devices=volume_backend_name-1,volume_backend_name-2.... + replication_device = device_target_id:vendor-id-info,managed_backend_name:biz,unique_key:val.... + +The use of multiopt will result in self.configuration.get('replication_device') +returning a list of properly formed python dictionaries that can +be easily consumed:: + + [{device_target_id: blahblah, managed_backend_name: biz, unique_key: val1}] -Notice that the only change from the usual driver configuration -section here is the addition of the replication_devices option. +In the case of multiple replication target devices:: + + ..... + [driver-biz] + volume_driver=xxxx + volume_backend_name=biz + + [driver-baz] + volume_driver=xxxx + volume_backend_name=baz + + [driver-foo] + volume_driver=xxxx + volume_backend_name=foo + managed_replication_target=True + replication_device = device_target_id:vendor-id-info,managed_backend_name:biz,unique_key:val.... + replication_device = device_target_id:vendor-id-info,managed_backend_name:baz,unique_key:val.... -Unmanaged target device ------------------------- +In this example the result is self.configuration.get('replication_device') +returning a list of properly formed python dictionaries:: -In some cases the replication target device may not be a -configured Cinder backend. In this case it's the configured -drivers responsibility to route commands to the active device -and to update provider info to ensure the proper iSCSI targets -are being used. + [{device_target_id: blahblah, managed_backend_name: biz, unique_key: val1}, + {device_target_id: moreblah, managed_backend_name: baz, unique_key: val1}] -This type of config changes only slightly, and instead of using -a backend_name, it takes the vendor unique config options:: + +In the case of unmanaged replication target devices:: ..... + [driver-biz] + volume_driver=xxxx + volume_backend_name=biz + + [driver-baz] + volume_driver=xxxx + volume_backend_name=baz + [driver-foo] volume_driver=xxxx volume_backend_name=foo - managed_replication_target=False - replication_devices={'remote_device_id'='vendor-id-of-remote-backend', - 'key1'='val1' 'key2'='val2' ...}, - {'remote_device_id'='vendor-id-of-remote-backend', - 'key7'='val7'....},... + replication_device = device_target_id:vendor-id-info,managed_backend_name:None,unique_key:val.... + replication_device = device_target_id:vendor-id-info,managed_backend_name:None,unique_key:val.... + +The managed_backend_name entry may also be omitted altogether in the case of unmanaged targets. + +In this example the result is self.configuration.get('replication_device) with the list:: + + [{device_target_id: blahblah, managed_backend_name: None, unique_key: val1}, + {device_target_id: moreblah, managed_backend_name: None, unique_key: val1}] + + + +Special note about Managed target device +---------------------------------------- +Remember that in the case where another Cinder backend is +used that it's likely you'll still need some special data +to instruct the primary driver how to communicate with the +secondary. In this case we use the same structure and entries +but we set the key **managed_backend_name** to a valid +Cinder backend name. + +**WARNING** +The building of the host string for a driver is not always +very straight forward. The enabled_backends names which +correspond to the driver-section are what actually get used +to form the host string for the volume service. + +Also, take care that your driver knows how to parse out the +host correctly, although the secondary backend may be managed +it may not be on the same host, it may have a pool specification +etc. In the example above we can assume the same host, in other +cases we would need to use the form:: + + @ -Note the key/value entries can be whatever the device requires, we treat the actual -variable in the config parser as a comma delimited list, the {} and = notations are -convenient/common parser delimeters, and the K/V entries are space seperated. +and for some vendors we may require pool specification:: -We provide a literal evaluator to convert these entries into a proper dict, thus -format is extremely important here. + @# +Regardless, it's best that you actually check the services entry +and verify that you've set this correctly, and likely to avoid +problems your vendor documentation for customers to configure this +should recommend configuring backends, then verifying settings +from cinder services list. Volume Types / Extra Specs --------------------------- @@ -101,7 +158,7 @@ If you needed to provide a specific backend device (multiple backends supporting Additionally you could provide additional details using scoped keys:: {replication: enabled, volume_backend_name: foo, - replication:replication_type: async} + replication: replication_type: async} Again, it's up to the driver to parse the volume type info on create and set things up as requested. While the scoping key can be anything, it's strongly recommended that all @@ -153,7 +210,7 @@ act as a toggle, allowing to switch back and forth betweeen primary and secondar **list_replication_targets** Used by the admin to query a volume for a list of configured replication targets -The expected return for this call is expeceted to mimic the form used in the config file. +The expected return for this call is expected to mimic the form used in the config file. For a volume replicating to managed replication targets::