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
This commit is contained in:
parent
7fb767f2d6
commit
5ee2efe223
@ -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:<required>,"
|
||||
"managed_backend_name:<host@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 '
|
||||
|
@ -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:<vendor-identifier-for-rep-target>
|
||||
* managed_backend_name:<cinder-backend-host-entry>,"
|
||||
|
||||
|
||||
An example config entry for a managed replication device
|
||||
would look like this::
|
||||
|
||||
.....
|
||||
[driver-biz]
|
||||
@ -45,43 +46,99 @@ 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....
|
||||
|
||||
Notice that the only change from the usual driver configuration
|
||||
section here is the addition of the replication_devices option.
|
||||
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}]
|
||||
|
||||
|
||||
Unmanaged target device
|
||||
------------------------
|
||||
|
||||
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.
|
||||
|
||||
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 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=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'....},...
|
||||
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....
|
||||
|
||||
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.
|
||||
In this example the result is self.configuration.get('replication_device')
|
||||
returning a list of properly formed python dictionaries::
|
||||
|
||||
We provide a literal evaluator to convert these entries into a proper dict, thus
|
||||
format is extremely important here.
|
||||
[{device_target_id: blahblah, managed_backend_name: biz, unique_key: val1},
|
||||
{device_target_id: moreblah, managed_backend_name: baz, unique_key: val1}]
|
||||
|
||||
|
||||
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
|
||||
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::
|
||||
|
||||
<host>@<driver-section-name>
|
||||
|
||||
and for some vendors we may require pool specification::
|
||||
|
||||
<host>@<driver-section-name>#<pool-name>
|
||||
|
||||
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
|
||||
---------------------------
|
||||
In order for a user to specify they'd like a replicated volume, there needs to be
|
||||
@ -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::
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user