From b8c756ecdd7cced1db4300935484e8c83701c82e Mon Sep 17 00:00:00 2001 From: WuKong Date: Tue, 30 Jun 2015 18:47:29 +0200 Subject: migrate moon code from github to opnfv Change-Id: Ice53e368fd1114d56a75271aa9f2e598e3eba604 Signed-off-by: WuKong --- keystone-moon/doc/source/extension_development.rst | 303 +++++++++++++++++++++ 1 file changed, 303 insertions(+) create mode 100644 keystone-moon/doc/source/extension_development.rst (limited to 'keystone-moon/doc/source/extension_development.rst') diff --git a/keystone-moon/doc/source/extension_development.rst b/keystone-moon/doc/source/extension_development.rst new file mode 100644 index 00000000..a0248495 --- /dev/null +++ b/keystone-moon/doc/source/extension_development.rst @@ -0,0 +1,303 @@ +.. + Licensed under the Apache License, Version 2.0 (the "License"); you may + not use this file except in compliance with the License. You may obtain + a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations + under the License. + +===================================== +Keystone Extensions Development Guide +===================================== + +General +======= + +This Extension Development Guide provides some mocked code to use as an +Extension code base in the ``keystone/contrib/example`` folder. + +- All Extensions must be created in the ``keystone/contrib`` folder. +- The new Extension code must be contained in a new folder under ``contrib``. +- Whenever possible an Extension should follow the following directory + structure convention:: + + keystone/contrib/ + └── my_extension +    ├── backends (optional) +    │   ├── __init__.py (mandatory) +    │   └── sql.py (optional) +    │   └── kvs.py (optional) +    ├── migrate_repo (optional) +    │   ├── __init__.py (mandatory) +    │   ├── migrate.cfg (mandatory) +    │   └── versions (mandatory) + │      ├── 001_create_tables.py (mandatory) + │      └── __init__.py (mandatory) +    ├── __init__.py (mandatory) +    ├── core.py (mandatory) +    ├── controllers.py (mandatory for API Extension) +    └── routers.py (mandatory for API Extension) + +- If the Extension implements an API Extension the ``controllers.py`` and + ``routers.py`` must be present and correctly handle the API Extension + requests and responses. +- If the Extension implements backends a ``backends`` folder should exist. + Backends are defined to store data persistently and can use a variety of + technologies. Please see the Backends section in this document for more info. +- If the Extension adds data structures, then a ``migrate_repo`` folder should + exist. +- If configuration changes are required/introduced in the + ``keystone.conf.sample`` file, these should be kept disabled as default and + have their own section. +- If configuration changes are required/introduced in the + ``keystone-paste.ini``, the new filter must be declared. +- The module may register to listen to events by declaring the corresponding + callbacks in the ``core.py`` file. +- The new extension should be disabled by default (it should not affect the + default application pipelines). + +Modifying the `keystone.conf.sample` File +========================================= + +In the case an Extension needs to change the ``keystone.conf.sample`` file, it +must follow the config file conventions and introduce a dedicated section. + +Example:: + + [example] + driver = keystone.contrib.example.backends.sql.mySQLClass + + [my_other_extension] + extension_flag = False + +The Extension parameters expressed should be commented out since, by default, +extensions are disabled. + +Example:: + + [example] + #driver = keystone.contrib.example.backends.sql.mySQLClass + + [my_other_extension] + #extension_flag = False + +In case the Extension is overriding or re-implementing an existing portion of +Keystone, the required change should be commented in the ``configuration.rst`` +but not placed in the `keystone.conf.sample` file to avoid unnecessary +confusion. + +Modifying the ``keystone-paste.ini`` File +========================================= + +In the case an Extension is augmenting a pipeline introducing a new ``filter`` +and/or APIs in the ``OS`` namespace, a corresponding ``filter:`` section is +necessary to be introduced in the ``keystone-paste.ini`` file. The Extension +should declare the filter factory constructor in the ``ini`` file. + +Example:: + + [filter:example] + paste.filter_factory = keystone.contrib.example.routers:ExampleRouter. + factory + +The ``filter`` must not be placed in the ``pipeline`` and treated as optional. +How to add the extension in the pipeline should be specified in detail in the +``configuration.rst`` file. + +Package Constructor File +======================== + +The ``__init__.py`` file represents the package constructor. Extension needs to +import what is necessary from the ``core.py`` module. + +Example: + +.. code-block:: python + + from keystone.contrib.example.core import * + +Core +==== + +The ``core.py`` file represents the main module defining the data structure and +interface. In the ``Model View Control`` (MVC) model it represents the +``Model`` part and it delegates to the ``Backends`` the data layer +implementation. + +In case the ``core.py`` file contains a ``Manager`` and a ``Driver`` it must +provide the dependency injections for the ``Controllers`` and/or other modules +using the ``Manager``. A good practice is to call the dependency +``extension_name_api``. + +Example: + +.. code-block:: python + + @dependency.provider('example_api') + class Manager(manager.Manager): + +Routers +======= + +``routers.py`` have the objective of routing the HTTP requests and direct them to +the correct method within the ``Controllers``. Extension routers are extending +the ``wsgi.ExtensionRouter``. + +Example: + +.. code-block:: python + + from keystone.common import wsgi + from keystone.contrib.example import controllers + + + class ExampleRouter(wsgi.ExtensionRouter): + + PATH_PREFIX = '/OS-EXAMPLE' + + def add_routes(self, mapper): + example_controller = controllers.ExampleV3Controller() + mapper.connect(self.PATH_PREFIX + '/example', + controller=example_controller, + action='do_something', + conditions=dict(method=['GET'])) + +Controllers +=========== + +``controllers.py`` have the objective of handing requests and implement the +Extension logic. Controllers are consumers of 'Managers' API and must have all +the dependency injections required. ``Controllers`` are extending the +``V3Controller`` class. + +Example: + +.. code-block:: python + + @dependency.requires('identity_api', 'example_api') + class ExampleV3Controller(controller.V3Controller): + pass + +Backends +======== + +The ``backends`` folder provides the model implementations for the different +backends supported by the Extension. See General above for an example directory +structure. + +If a SQL backend is provided, in the ``sql.py`` backend implementation it is +mandatory to define the new table(s) that the Extension introduces and the +attributes they are composed of. + +For more information on backends, refer to the `Keystone Architecture +`_ +documentation. + +Example: + +.. code-block:: python + + class ExampleSQLBackend(sql.ModelBase, sql.DictBase): + """example table description.""" + __tablename__ = 'example_table' + attributes = ['id', 'type', 'extra'] + + example_id = sql.Column(sql.String(64), + primary_key=True, + nullable=False) + ... + +SQL Migration Repository +======================== + +In case the Extension is adding SQL data structures, these must be stored in +separate tables and must not be included in the ``migrate_repo`` of the core +Keystone. Please refer to the ``migrate.cfg`` file to configure the Extension +repository. + +In order to create the Extension tables and their attributes, a ``db_sync`` +command must be executed. + +Example: + +.. code-block:: bash + + $ ./bin/keystone-manage db_sync --extension example + +Event Callbacks +--------------- + +Extensions may provide callbacks to Keystone (Identity) events. +Extensions must provide the list of events of interest and the corresponding +callbacks. Events are issued upon successful creation, modification, and +deletion of the following Keystone resources: + +- ``group`` +- ``project`` +- ``role`` +- ``user`` + +The extension's ``Manager`` class must contain the +``event_callbacks`` attribute. It is a dictionary listing as keys +those events that are of interest and the values should be the respective +callbacks. Event callback registration is done via the +dependency injection mechanism. During dependency provider registration, the +``dependency.provider`` decorator looks for the ``event_callbacks`` +class attribute. If it exists the event callbacks are registered +accordingly. In order to enable event callbacks, the extension's ``Manager`` +class must also be a dependency provider. + +Example: + +.. code-block:: python + + # Since this is a dependency provider. Any code module using this or any + # other dependency provider (uses the dependency.provider decorator) + # will be enabled for the attribute based notification + + @dependency.provider('example_api') + class ExampleManager(manager.Manager): + """Example Manager. + + See :mod:`keystone.common.manager.Manager` for more details on + how this dynamically calls the backend. + + """ + + def __init__(self): + self.event_callbacks = { + # Here we add the event_callbacks class attribute that + # calls project_deleted_callback when a project is deleted. + 'deleted': { + 'project': [ + self.project_deleted_callback]}} + super(ExampleManager, self).__init__( + 'keystone.contrib.example.core.ExampleDriver') + + def project_deleted_callback(self, context, message): + # cleanup data related to the deleted project here + +A callback must accept the following parameters: + +- ``service`` - the service information (e.g. identity) +- ``resource_type`` - the resource type (e.g. project) +- ``operation`` - the operation (updated, created, deleted) +- ``payload`` - the actual payload info of the resource that was acted on + +Current callback operations: + +- ``created`` +- ``deleted`` +- ``updated`` + +Example: + +.. code-block:: python + + def project_deleted_callback(self, service, resource_type, operation, + payload): -- cgit 1.2.3-korg