summaryrefslogtreecommitdiff
path: root/doc/source/reference/defining.rst
blob: d8e18660ca4e0bbe9c4613c930275a6713312727 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
==================
 Defining Options
==================

Configuration options may be set on the command line, in the
:mod:`environment <oslo_config.sources._environment>`, or in config files.
Options are processed in that order.

The schema for each option is defined using the
:class:`Opt` class or its sub-classes, for example:

.. code-block:: python

    from oslo_config import cfg
    from oslo_config import types

    PortType = types.Integer(1, 65535)

    common_opts = [
        cfg.StrOpt('bind_host',
                   default='0.0.0.0',
                   help='IP address to listen on.'),
        cfg.Opt('bind_port',
                type=PortType,
                default=9292,
                help='Port number to listen on.')
    ]

Option Types
------------

Options can have arbitrary types via the `type` parameter to the :class:`Opt`
constructor. The `type` parameter is a callable object that takes a string and
either returns a value of that particular type or raises :class:`ValueError` if
the value can not be converted.

For convenience, there are predefined option subclasses in
:mod:`oslo_config.cfg` that set the option `type` as in the following table:

======================================  ======
Type                                    Option
======================================  ======
:class:`oslo_config.types.String`       :class:`oslo_config.cfg.StrOpt`
:class:`oslo_config.types.String`       :class:`oslo_config.cfg.SubCommandOpt`
:class:`oslo_config.types.Boolean`      :class:`oslo_config.cfg.BoolOpt`
:class:`oslo_config.types.Integer`      :class:`oslo_config.cfg.IntOpt`
:class:`oslo_config.types.Float`        :class:`oslo_config.cfg.FloatOpt`
:class:`oslo_config.types.Port`         :class:`oslo_config.cfg.PortOpt`
:class:`oslo_config.types.List`         :class:`oslo_config.cfg.ListOpt`
:class:`oslo_config.types.Dict`         :class:`oslo_config.cfg.DictOpt`
:class:`oslo_config.types.IPAddress`    :class:`oslo_config.cfg.IPOpt`
:class:`oslo_config.types.Hostname`     :class:`oslo_config.cfg.HostnameOpt`
:class:`oslo_config.types.HostAddress`  :class:`oslo_config.cfg.HostAddressOpt`
:class:`oslo_config.types.URI`          :class:`oslo_config.cfg.URIOpt`
======================================  ======

For :class:`oslo_config.cfg.MultiOpt` the `item_type` parameter defines
the type of the values. For convenience, :class:`oslo_config.cfg.MultiStrOpt`
is :class:`~oslo_config.cfg.MultiOpt` with the `item_type` parameter set to
:class:`oslo_config.types.MultiString`.

The following example defines options using the convenience classes:

.. code-block:: python

    enabled_apis_opt = cfg.ListOpt('enabled_apis',
                                   default=['ec2', 'osapi_compute'],
                                   help='List of APIs to enable by default.')

    DEFAULT_EXTENSIONS = [
        'nova.api.openstack.compute.contrib.standard_extensions'
    ]
    osapi_compute_extension_opt = cfg.MultiStrOpt('osapi_compute_extension',
                                                  default=DEFAULT_EXTENSIONS)

Registering Options
-------------------

Option schemas are registered with the config manager at runtime, but before
the option is referenced:

.. code-block:: python

    class ExtensionManager(object):

        enabled_apis_opt = cfg.ListOpt(...)

        def __init__(self, conf):
            self.conf = conf
            self.conf.register_opt(enabled_apis_opt)
            ...

        def _load_extensions(self):
            for ext_factory in self.conf.osapi_compute_extension:
                ....

A common usage pattern is for each option schema to be defined in the module or
class which uses the option:

.. code-block:: python

    opts = ...

    def add_common_opts(conf):
        conf.register_opts(opts)

    def get_bind_host(conf):
        return conf.bind_host

    def get_bind_port(conf):
        return conf.bind_port

An option may optionally be made available via the command line. Such options
must be registered with the config manager before the command line is parsed
(for the purposes of --help and CLI arg validation).

Note that options registered for CLI use do not need to be registered again for
use from other config sources, such as files. CLI options can be read from
either the CLI or from the other enabled config sources.

.. code-block:: python

    cli_opts = [
        cfg.BoolOpt('verbose',
                    short='v',
                    default=False,
                    help='Print more verbose output.'),
        cfg.BoolOpt('debug',
                    short='d',
                    default=False,
                    help='Print debugging output.'),
    ]

    def add_common_opts(conf):
        conf.register_cli_opts(cli_opts)

Option Groups
-------------

Options can be registered as belonging to a group:

.. code-block:: python

    rabbit_group = cfg.OptGroup(name='rabbit',
                                title='RabbitMQ options')

    rabbit_host_opt = cfg.StrOpt('host',
                                 default='localhost',
                                 help='IP/hostname to listen on.'),
    rabbit_port_opt = cfg.PortOpt('port',
                                  default=5672,
                                  help='Port number to listen on.')

    def register_rabbit_opts(conf):
        conf.register_group(rabbit_group)
        # options can be registered under a group in either of these ways:
        conf.register_opt(rabbit_host_opt, group=rabbit_group)
        conf.register_opt(rabbit_port_opt, group='rabbit')

If no group attributes are required other than the group name, the group
need not be explicitly registered for example:

.. code-block:: python

    def register_rabbit_opts(conf):
        # The group will automatically be created, equivalent calling:
        #   conf.register_group(OptGroup(name='rabbit'))
        conf.register_opt(rabbit_port_opt, group='rabbit')

If no group is specified, options belong to the 'DEFAULT' section of config
files:

.. code-block:: text

    glance-api.conf:
      [DEFAULT]
      bind_port = 9292
      ...

      [rabbit]
      host = localhost
      port = 5672
      use_ssl = False
      userid = guest
      password = guest
      virtual_host = /

Command-line options in a group are automatically prefixed with the
group name:

.. code-block:: console

    --rabbit-host localhost --rabbit-port 9999

Dynamic Groups
--------------

Groups can be registered dynamically by application code. This
introduces a challenge for the sample generator, discovery mechanisms,
and validation tools, since they do not know in advance the names of
all of the groups. The ``dynamic_group_owner`` parameter to the
constructor specifies the full name of an option registered in another
group that controls repeated instances of a dynamic group. This option
is usually a MultiStrOpt.

For example, Cinder supports multiple storage backend devices and
services. To configure Cinder to communicate with multiple backends,
the ``enabled_backends`` option is set to the list of names of
backends. Each backend group includes the options for communicating
with that device or service.

Driver Groups
-------------

Groups can have dynamic sets of options, usually based on a driver
that has unique requirements. This works at runtime because the code
registers options before it uses them, but it introduces a challenge
for the sample generator, discovery mechanisms, and validation tools
because they do not know in advance the correct options for a group.

To address this issue, the driver option for a group can be named
using the ``driver_option`` parameter.  Each driver option should
define its own discovery entry point namespace to return the set of
options for that driver, named using the prefix
``"oslo.config.opts."`` followed by the driver option name.

In the Cinder case described above, a ``volume_backend_name`` option
is part of the static definition of the group, so ``driver_option``
should be set to ``"volume_backend_name"``. And plugins should be
registered under ``"oslo.config.opts.volume_backend_name"`` using the
same names as the main plugin registered with
``"oslo.config.opts"``. The drivers residing within the Cinder code
base have an entry point named ``"cinder"`` registered.

Special Handling Instructions
-----------------------------

Options may be declared as required so that an error is raised if the user
does not supply a value for the option:

.. code-block:: python

    opts = [
        cfg.StrOpt('service_name', required=True),
        cfg.StrOpt('image_id', required=True),
        ...
    ]

Options may be declared as secret so that their values are not leaked into
log files:

.. code-block:: python

     opts = [
        cfg.StrOpt('s3_store_access_key', secret=True),
        cfg.StrOpt('s3_store_secret_key', secret=True),
        ...
     ]

Dictionary Options
------------------

If you need end users to specify a dictionary of key/value pairs, then you can
use the DictOpt:

.. code-block:: python

    opts = [
        cfg.DictOpt('foo',
                    default={})
    ]

The end users can then specify the option foo in their configuration file
as shown below:

.. code-block:: ini

    [DEFAULT]
    foo = k1:v1,k2:v2

Advanced Option
---------------

Use if you need to label an option as advanced in sample files, indicating the
option is not normally used by the majority of users and might have a
significant effect on stability and/or performance:

.. code-block:: python

    from oslo_config import cfg

    opts = [
        cfg.StrOpt('option1', default='default_value',
                    advanced=True, help='This is help '
                    'text.'),
        cfg.PortOpt('option2', default='default_value',
                     help='This is help text.'),
    ]

    CONF = cfg.CONF
    CONF.register_opts(opts)

This will result in the option being pushed to the bottom of the
namespace and labeled as advanced in the sample files, with a notation
about possible effects:

.. code-block:: ini

    [DEFAULT]
    ...
    # This is help text. (string value)
    # option2 = default_value
    ...
    <pushed to bottom of section>
    ...
    # This is help text. (string value)
    # Advanced Option: intended for advanced users and not used
    # by the majority of users, and might have a significant
    # effect on stability and/or performance.
    # option1 = default_value