├── .coveragerc ├── .gitignore ├── .gitreview ├── .pre-commit-config.yaml ├── .stestr.conf ├── .zuul.yaml ├── CONTRIBUTING.rst ├── HACKING.rst ├── LICENSE ├── README.rst ├── bindep.txt ├── doc ├── requirements.txt └── source │ ├── admin │ ├── drivers.rst │ ├── index.rst │ ├── kafka.rst │ └── rabbit.rst │ ├── conf.py │ ├── configuration │ ├── conffixture.rst │ ├── index.rst │ └── opts.rst │ ├── contributor │ ├── contributing.rst │ ├── driver-dev-guide.rst │ ├── index.rst │ ├── supported-messaging-drivers.rst │ └── using-simulator.rst │ ├── index.rst │ ├── reference │ ├── exceptions.rst │ ├── executors.rst │ ├── index.rst │ ├── notification_driver.rst │ ├── notification_listener.rst │ ├── notifier.rst │ ├── rpcclient.rst │ ├── serializer.rst │ ├── server.rst │ ├── target.rst │ └── transport.rst │ └── user │ ├── FAQ.rst │ ├── history.rst │ └── index.rst ├── etc └── routing_notifier.yaml.sample ├── oslo_messaging ├── __init__.py ├── _drivers │ ├── __init__.py │ ├── amqp.py │ ├── amqpdriver.py │ ├── base.py │ ├── common.py │ ├── impl_fake.py │ ├── impl_kafka.py │ ├── impl_rabbit.py │ ├── kafka_driver │ │ ├── __init__.py │ │ └── kafka_options.py │ └── pool.py ├── _metrics │ ├── __init__.py │ └── client.py ├── _utils.py ├── conffixture.py ├── dispatcher.py ├── exceptions.py ├── hacking │ ├── __init__.py │ └── checks.py ├── notify │ ├── __init__.py │ ├── _impl_log.py │ ├── _impl_noop.py │ ├── _impl_routing.py │ ├── _impl_test.py │ ├── dispatcher.py │ ├── filter.py │ ├── listener.py │ ├── log_handler.py │ ├── logger.py │ ├── messaging.py │ ├── middleware.py │ └── notifier.py ├── opts.py ├── rpc │ ├── __init__.py │ ├── client.py │ ├── dispatcher.py │ ├── server.py │ └── transport.py ├── serializer.py ├── server.py ├── target.py ├── tests │ ├── __init__.py │ ├── drivers │ │ ├── __init__.py │ │ ├── test_impl_kafka.py │ │ ├── test_impl_rabbit.py │ │ └── test_pool.py │ ├── functional │ │ ├── __init__.py │ │ ├── notify │ │ │ ├── __init__.py │ │ │ └── test_logger.py │ │ ├── test_functional.py │ │ ├── test_rabbitmq.py │ │ └── utils.py │ ├── notify │ │ ├── __init__.py │ │ ├── test_dispatcher.py │ │ ├── test_listener.py │ │ ├── test_log_handler.py │ │ ├── test_logger.py │ │ ├── test_middleware.py │ │ └── test_notifier.py │ ├── rpc │ │ ├── __init__.py │ │ ├── test_client.py │ │ ├── test_dispatcher.py │ │ └── test_server.py │ ├── test_config_opts_proxy.py │ ├── test_exception_serialization.py │ ├── test_expected_exceptions.py │ ├── test_fixture.py │ ├── test_opts.py │ ├── test_target.py │ ├── test_transport.py │ ├── test_urls.py │ ├── test_utils.py │ └── utils.py ├── transport.py └── version.py ├── releasenotes ├── notes │ ├── RPC-call-monitoring-7977f047d069769a.yaml │ ├── add-enable_cancel_on_failover-22ac472b93dd3a23.yaml │ ├── add-ping-endpoint.yaml │ ├── add-quorum-control-configurations-beed79811ff97ba2.yaml │ ├── add-ssl-support-for-kafka.yaml │ ├── add_reno-3b4ae0789e9c45b4.yaml │ ├── adding_support_for_quorum_queues-3101d055b492289e.yaml │ ├── allow-transient-no-expire-ce7ae9d8c9d15751.yaml │ ├── auto-deleted-failed-quorum-ca6a3923c3ed999a.yaml │ ├── blocking-executor-deprecated-895146c1c3bf2f51.yaml │ ├── blocking-executor-support-dropped-a3bc74c6825863f0.yaml │ ├── bug-1917645-rabbit-use-retry-parameter-for-notifications-3f7c508ab4437579.yaml │ ├── bug-1981093-kafka-dont-log-in-tpool-execute-fa50ceee2d55ebae.yaml │ ├── bug-1993149-e8b231791b65e938.yaml │ ├── bug-2068630-6ff92f213bc4eca0.yaml │ ├── bug-2098714-d55094fa4fbb3178.yaml │ ├── bump-amqp-version-due-to-tls-issue-e877b152eb101c15.yaml │ ├── connection_ttl-2cf0fe6e1ab8c73c.yaml │ ├── declare_fallback_durable_exchange-0db677de4fdf1e78.yaml │ ├── deprecate-ZeroMQ-driver-a8af25aaba867c5b.yaml │ ├── deprecate-eventlet-executor-13835b9818fd77f2.yaml │ ├── deprecate-the-option-heartbeat_in_pthread-from-rabbit-driver-5757adb83701caa5.yaml │ ├── deprecated-amqp1-driver-4bf57449bc2b7aad.yaml │ ├── disable-mandatory-flag-a6210a534f3853f0.yaml │ ├── do-not-run-heartbeat-in-pthread-by-default-42e1299f59b841f8.yaml │ ├── drop-python27-support-5ef2f365d8930483.yaml │ ├── enforce_fips_mode-07dd259eb8a73c2b.yaml │ ├── fix-access_policy-deafult-a6954a147cb002b0.yaml │ ├── get-rpc-client-0b4aa62160864b29.yaml │ ├── get-rpc-helpers-cls-8911826ac08aef2a.yaml │ ├── get_rpc_transport-4aa3511ad9754a60.yaml │ ├── handle-missing-queue-553a803f94976be7.yaml │ ├── heartbeat-rate-3-7ada9edbccc11a3f.yaml │ ├── kafka-client-library-change-fe16d5a34550db7f.yaml │ ├── kombo-reconnect-splay-a81eb5fca6180510.yaml │ ├── no-log-if-ignore-errors-e2223b8a646b4c40.yaml │ ├── option-rabbitmq-max_retries-has-been-deprecated-471f66a9e6d672a2.yaml │ ├── oslo-metrics-support-fe16343a637cc14b.yaml │ ├── pika-driver-has-been-deprecated-e2407fa53c91fe5c.yaml │ ├── rabbit-no-wait-for-ack-9e5de3e1320d7660.yaml │ ├── rabbit_queue_manager-363209285cbbe257.yaml │ ├── rabbit_quorum_typo-9c06a9fd8d767f53.yaml │ ├── rabbit_transient_quorum-fc3c3f88ead90034.yaml │ ├── rabbitmq-opts-cleanup-e0f97d4cc0855c5a.yaml │ ├── removal-deprecated-options-6d4c5db90525c52d.yaml │ ├── remove-RequestContextSerializer-234c0496a7e0376b.yaml │ ├── remove-ZeroMQ-driver-e9e0bbbb7bd4f5e6.yaml │ ├── remove-amqp1-c924ea548dadffaa.yaml │ ├── remove-deprecated-notif-opts-142f8eea540c17ec.yaml │ ├── remove-kafka-conn-pool-opts-0b7962e2f22b24ed.yaml │ ├── remove-old-quorum-opts-with-typo-5e013064fb6df062.yaml │ ├── remove-pika-1bae204ced2521a3.yaml │ ├── remove-py38-381f832001230756.yaml │ ├── reply_q-timeout-e3c3bae636e8bc74.yaml │ ├── retry-support-07996ef04dda9482.yaml │ ├── run-heartbeat-in-pthread-by-default-28637b41ebf500dc.yaml │ ├── stream-c3dd31ee98f6bbd7.yaml │ ├── undeprecate_heartbeat_in_pthread-48e2c1fc008cf208.yaml │ └── use-extras-for-optional-deps-2a00e8007ef7a629.yaml └── source │ ├── 2023.1.rst │ ├── 2023.2.rst │ ├── 2024.1.rst │ ├── 2024.2.rst │ ├── 2025.1.rst │ ├── _static │ └── .placeholder │ ├── _templates │ └── .placeholder │ ├── conf.py │ ├── index.rst │ ├── locale │ └── en_GB │ │ └── LC_MESSAGES │ │ └── releasenotes.po │ ├── newton.rst │ ├── ocata.rst │ ├── pike.rst │ ├── queens.rst │ ├── rocky.rst │ ├── stein.rst │ ├── train.rst │ ├── unreleased.rst │ ├── ussuri.rst │ ├── victoria.rst │ ├── wallaby.rst │ ├── xena.rst │ ├── yoga.rst │ └── zed.rst ├── requirements.txt ├── setup.cfg ├── setup.py ├── test-requirements.txt ├── tools ├── functions.sh ├── messages_length.yaml ├── setup-scenario-env.sh └── simulator.py └── tox.ini /.coveragerc: -------------------------------------------------------------------------------- 1 | [run] 2 | branch = True 3 | source = oslo_messaging 4 | omit = oslo_messaging/tests/* 5 | 6 | [report] 7 | ignore_errors = True 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Add patterns in here to exclude files created by tools integrated with this 2 | # repository, such as test frameworks from the project's recommended workflow, 3 | # rendered documentation and package builds. 4 | # 5 | # Don't add patterns to exclude files created by preferred personal tools 6 | # (editors, IDEs, your operating system itself even). These should instead be 7 | # maintained outside the repository, for example in a ~/.gitignore file added 8 | # with: 9 | # 10 | # git config --global core.excludesfile '~/.gitignore' 11 | 12 | AUTHORS 13 | ChangeLog 14 | *.pyc 15 | *.log 16 | .tox 17 | .coverage 18 | *.egg-info/ 19 | .eggs 20 | *.egg 21 | build/ 22 | doc/build/ 23 | doc/source/api/ 24 | dist/ 25 | .stestr/ 26 | RELEASENOTES.rst 27 | releasenotes/notes/reno.cache 28 | releasenotes/build 29 | cover/ 30 | .venv/ 31 | oslo.messaging.conf 32 | -------------------------------------------------------------------------------- /.gitreview: -------------------------------------------------------------------------------- 1 | [gerrit] 2 | host=review.opendev.org 3 | port=29418 4 | project=openstack/oslo.messaging.git 5 | -------------------------------------------------------------------------------- /.pre-commit-config.yaml: -------------------------------------------------------------------------------- 1 | repos: 2 | - repo: https://github.com/pre-commit/pre-commit-hooks 3 | rev: v5.0.0 4 | hooks: 5 | - id: trailing-whitespace 6 | # Replaces or checks mixed line ending 7 | - id: mixed-line-ending 8 | args: ['--fix', 'lf'] 9 | exclude: '.*\.(svg)$' 10 | # Forbid files which have a UTF-8 byte-order marker 11 | - id: check-byte-order-marker 12 | # Checks that non-binary executables have a proper shebang 13 | - id: check-executables-have-shebangs 14 | # Check for files that contain merge conflict strings. 15 | - id: check-merge-conflict 16 | # Check for debugger imports and py37+ breakpoint() 17 | # calls in python source 18 | - id: debug-statements 19 | - id: check-yaml 20 | files: .*\.(yaml|yml)$ 21 | - repo: https://opendev.org/openstack/hacking 22 | rev: 7.0.0 23 | hooks: 24 | - id: hacking 25 | additional_dependencies: [] 26 | - repo: https://github.com/PyCQA/bandit 27 | rev: 1.7.10 28 | hooks: 29 | - id: bandit 30 | args: ['-x', 'tests,tools'] 31 | - repo: https://github.com/asottile/pyupgrade 32 | rev: v3.18.0 33 | hooks: 34 | - id: pyupgrade 35 | args: [--py3-only] 36 | -------------------------------------------------------------------------------- /.stestr.conf: -------------------------------------------------------------------------------- 1 | [DEFAULT] 2 | test_path=./oslo_messaging/tests 3 | top_path=./ 4 | -------------------------------------------------------------------------------- /.zuul.yaml: -------------------------------------------------------------------------------- 1 | - job: 2 | name: oslo.messaging-tox-py310-func-scenario01 3 | parent: openstack-tox-py310 4 | vars: 5 | tox_envlist: py310-func-scenario01 6 | bindep_profile: rabbit 7 | 8 | - job: 9 | name: oslo.messaging-tox-py310-func-scenario02 10 | parent: openstack-tox-py310 11 | vars: 12 | tox_envlist: py310-func-scenario02 13 | bindep_profile: rabbit kafka 14 | 15 | # Begin v3 native jobs 16 | # See https://docs.openstack.org/devstack/latest/ 17 | 18 | - job: 19 | name: oslo.messaging-devstack-tempest-full-base 20 | description: | 21 | Base for all devstack based tempest full testing jobs (with 22 | neutron) 23 | abstract: true 24 | parent: devstack-tempest 25 | timeout: 10800 26 | required-projects: 27 | - openstack/oslo.messaging 28 | vars: 29 | tox_envlist: full 30 | 31 | - job: 32 | name: oslo.messaging-src-dsvm-full-rabbit 33 | description: | 34 | Run full tempest tests against rabbitmq 35 | parent: oslo.messaging-devstack-tempest-full-base 36 | 37 | - job: 38 | name: oslo.messaging-src-dsvm-full-kafka-hybrid 39 | description: | 40 | Run the full tempest tests using Apache Kafka for Notifications. 41 | parent: oslo.messaging-devstack-tempest-full-base 42 | required-projects: 43 | - openstack/devstack-plugin-kafka 44 | vars: 45 | devstack_plugins: 46 | devstack-plugin-kafka: https://opendev.org/openstack/devstack-plugin-kafka 47 | zuul_copy_output: 48 | '{{ devstack_log_dir }}/server.log': logs 49 | 50 | - job: 51 | name: oslo.messaging-grenade 52 | parent: grenade 53 | timeout: 10800 54 | required-projects: 55 | - openstack/oslo.messaging 56 | irrelevant-files: 57 | - ^.*\.rst$ 58 | - ^doc/.*$ 59 | - ^releasenotes/.*$ 60 | - ^.git.*$ 61 | - ^(test-|)requirements.txt$ 62 | - ^setup.cfg$ 63 | - ^\.pre-commit-config\.yaml$ 64 | 65 | - job: 66 | name: oslo.messaging-grenade-multinode 67 | parent: grenade-multinode 68 | timeout: 10800 69 | required-projects: 70 | - openstack/oslo.messaging 71 | irrelevant-files: 72 | - ^.*\.rst$ 73 | - ^doc/.*$ 74 | - ^releasenotes/.*$ 75 | - ^.git.*$ 76 | - ^(test-|)requirements.txt$ 77 | - ^setup.cfg$ 78 | - ^\.pre-commit-config\.yaml$ 79 | 80 | - project: 81 | templates: 82 | - check-requirements 83 | - lib-forward-testing-python3 84 | - openstack-cover-jobs 85 | - openstack-python3-jobs 86 | - periodic-stable-jobs 87 | - publish-openstack-docs-pti 88 | - release-notes-jobs-python3 89 | check: 90 | jobs: 91 | - oslo.messaging-tox-py310-func-scenario01 92 | - oslo.messaging-tox-py310-func-scenario02: 93 | voting: false 94 | - oslo.messaging-src-dsvm-full-rabbit 95 | - oslo.messaging-src-dsvm-full-kafka-hybrid: 96 | voting: false 97 | - oslo.messaging-grenade: 98 | voting: false 99 | - oslo.messaging-grenade-multinode: 100 | voting: false 101 | gate: 102 | jobs: 103 | - oslo.messaging-tox-py310-func-scenario01 104 | - oslo.messaging-src-dsvm-full-rabbit 105 | -------------------------------------------------------------------------------- /CONTRIBUTING.rst: -------------------------------------------------------------------------------- 1 | If you would like to contribute to the development of oslo's libraries, 2 | first you must take a look to this page: 3 | 4 | https://specs.openstack.org/openstack/oslo-specs/specs/policy/contributing.html 5 | 6 | If you would like to contribute to the development of OpenStack, 7 | you must follow the steps in this page: 8 | 9 | https://docs.openstack.org/infra/manual/developers.html 10 | 11 | Once those steps have been completed, changes to OpenStack 12 | should be submitted for review via the Gerrit tool, following 13 | the workflow documented at: 14 | 15 | https://docs.openstack.org/infra/manual/developers.html#development-workflow 16 | 17 | Pull requests submitted through GitHub will be ignored. 18 | 19 | Bugs should be filed on Launchpad, not GitHub: 20 | 21 | https://bugs.launchpad.net/oslo.messaging 22 | -------------------------------------------------------------------------------- /HACKING.rst: -------------------------------------------------------------------------------- 1 | Style Commandments 2 | ================== 3 | 4 | Read the OpenStack Style Commandments https://docs.openstack.org/hacking/latest/ 5 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Team and repository tags 3 | ======================== 4 | 5 | .. image:: https://governance.openstack.org/tc/badges/oslo.messaging.svg 6 | :target: https://governance.openstack.org/tc/reference/tags/index.html 7 | 8 | .. Change things from this point on 9 | 10 | Oslo Messaging Library 11 | ====================== 12 | 13 | .. image:: https://img.shields.io/pypi/v/oslo.messaging.svg 14 | :target: https://pypi.org/project/oslo.messaging/ 15 | :alt: Latest Version 16 | 17 | .. image:: https://img.shields.io/pypi/dm/oslo.messaging.svg 18 | :target: https://pypi.org/project/oslo.messaging/ 19 | :alt: Downloads 20 | 21 | The Oslo messaging API supports RPC and notifications over a number of 22 | different messaging transports. 23 | 24 | * License: Apache License, Version 2.0 25 | * Documentation: https://docs.openstack.org/oslo.messaging/latest/ 26 | * Source: https://opendev.org/openstack/oslo.messaging 27 | * Bugs: https://bugs.launchpad.net/oslo.messaging 28 | * Release notes: https://docs.openstack.org/releasenotes/oslo.messaging/ 29 | -------------------------------------------------------------------------------- /bindep.txt: -------------------------------------------------------------------------------- 1 | # common dpkg 2 | gettext [platform:dpkg] # For releasenotes job 3 | build-essential [platform:dpkg] 4 | libffi-dev [platform:dpkg] 5 | 6 | # common rpm 7 | gcc [platform:rpm] 8 | gcc-c++ [platform:rpm] 9 | make [platform:rpm] 10 | pkgconfig [platform:rpm] 11 | libffi-devel [platform:rpm] 12 | 13 | # RabbitMQ message broker 14 | rabbitmq-server [platform:dpkg rabbit] 15 | rabbitmq-server [platform:rpm rabbit] 16 | 17 | # kafka dpkg 18 | default-jdk [platform:dpkg kafka] 19 | librdkafka1 [platform:dpkg kafka] 20 | librdkafka-dev [platform:dpkg kafka] 21 | -------------------------------------------------------------------------------- /doc/requirements.txt: -------------------------------------------------------------------------------- 1 | openstackdocstheme>=2.2.0 # Apache-2.0 2 | sphinx>=2.0.0 # BSD 3 | reno>=3.1.0 # Apache-2.0 4 | 5 | # imported when the source code is parsed for generating documentation: 6 | fixtures>=3.0.0 # Apache-2.0/BSD 7 | confluent-kafka>=0.11.6 # Apache-2.0 8 | tenacity>=3.2.1 # Apache-2.0 9 | -------------------------------------------------------------------------------- /doc/source/admin/drivers.rst: -------------------------------------------------------------------------------- 1 | =================== 2 | Available Drivers 3 | =================== 4 | 5 | .. list-plugins:: oslo.messaging.drivers 6 | :detailed: 7 | -------------------------------------------------------------------------------- /doc/source/admin/index.rst: -------------------------------------------------------------------------------- 1 | ================ 2 | Deployment Guide 3 | ================ 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | drivers 9 | kafka 10 | rabbit 11 | -------------------------------------------------------------------------------- /doc/source/conf.py: -------------------------------------------------------------------------------- 1 | # Copyright (C) 2020 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | # 15 | # Configuration file for the Sphinx documentation builder. 16 | # 17 | # This file does only contain a selection of the most common options. For a 18 | # full list see the documentation: 19 | # http://www.sphinx-doc.org/en/master/config 20 | 21 | 22 | # -- Project information ------------------------------------------------------ 23 | 24 | # General information about the project. 25 | copyright = '2018, Oslo Contributors' 26 | 27 | # -- General configuration ---------------------------------------------------- 28 | 29 | # Add any Sphinx extension module names here, as strings. They can be 30 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 31 | extensions = [ 32 | 'sphinx.ext.autodoc', 33 | 'sphinx.ext.todo', 34 | 'openstackdocstheme', 35 | 'stevedore.sphinxext', 36 | 'oslo_config.sphinxext', 37 | ] 38 | 39 | # openstackdocstheme options 40 | openstackdocs_repo_name = 'openstack/oslo.messaging' 41 | openstackdocs_bug_project = 'oslo.messaging' 42 | openstackdocs_bug_tag = '' 43 | 44 | # The master toctree document. 45 | master_doc = 'index' 46 | 47 | # The name of the Pygments (syntax highlighting) style to use. 48 | pygments_style = 'native' 49 | 50 | # -- Options for HTML output -------------------------------------------------- 51 | 52 | # The theme to use for HTML and HTML Help pages. Major themes that come with 53 | # Sphinx are currently 'default' and 'sphinxdoc'. 54 | html_theme = 'openstackdocs' 55 | -------------------------------------------------------------------------------- /doc/source/configuration/conffixture.rst: -------------------------------------------------------------------------------- 1 | ---------------------- 2 | Testing Configurations 3 | ---------------------- 4 | 5 | .. currentmodule:: oslo_messaging.conffixture 6 | 7 | .. autoclass:: ConfFixture 8 | :members: 9 | 10 | -------------------------------------------------------------------------------- /doc/source/configuration/index.rst: -------------------------------------------------------------------------------- 1 | ============= 2 | Configuration 3 | ============= 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | opts 9 | conffixture 10 | -------------------------------------------------------------------------------- /doc/source/configuration/opts.rst: -------------------------------------------------------------------------------- 1 | ======================= 2 | Configuration Options 3 | ======================= 4 | 5 | oslo.messaging uses oslo.config to define and manage configuration 6 | options to allow the deployer to control how an application uses the 7 | underlying messaging system. 8 | 9 | .. show-options:: oslo.messaging 10 | 11 | API 12 | === 13 | 14 | .. currentmodule:: oslo_messaging.opts 15 | 16 | .. autofunction:: list_opts 17 | -------------------------------------------------------------------------------- /doc/source/contributor/contributing.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | Contributing 3 | ============== 4 | 5 | .. include:: ../../../CONTRIBUTING.rst 6 | -------------------------------------------------------------------------------- /doc/source/contributor/driver-dev-guide.rst: -------------------------------------------------------------------------------- 1 | --------------------------------------- 2 | Guide for Transport Driver Implementors 3 | --------------------------------------- 4 | 5 | .. currentmodule:: oslo_messaging 6 | 7 | .. automodule:: oslo_messaging._drivers.base 8 | 9 | ============ 10 | Introduction 11 | ============ 12 | 13 | This document is a *best practices* guide for the developer interested 14 | in creating a new transport driver for Oslo.Messaging. It should also 15 | be used by maintainers as a reference for proper driver behavior. 16 | This document will describe the driver interface and prescribe the 17 | expected behavior of any driver implemented to this interface. 18 | 19 | **Note well:** The API described in this document is internal to the 20 | oslo.messaging library and therefore **private**. Under no 21 | circumstances should this API be referenced by code external to the 22 | oslo.messaging library. 23 | 24 | ================ 25 | Driver Interface 26 | ================ 27 | 28 | The driver interface is defined by a set of abstract base classes. The 29 | developer creates a driver by defining concrete classes from these 30 | bases. The derived classes embody the logic that is specific for the 31 | messaging back-end that is to be supported. 32 | 33 | These base classes are defined in the *base.py* file in the *_drivers* 34 | subdirectory. 35 | 36 | =============== 37 | IncomingMessage 38 | =============== 39 | 40 | .. autoclass:: IncomingMessage 41 | :members: 42 | 43 | ================== 44 | RpcIncomingMessage 45 | ================== 46 | 47 | .. autoclass:: RpcIncomingMessage 48 | :members: 49 | 50 | ======== 51 | Listener 52 | ======== 53 | 54 | .. autoclass:: Listener 55 | :members: 56 | 57 | ================= 58 | PollStyleListener 59 | ================= 60 | 61 | .. autoclass:: PollStyleListener 62 | :members: 63 | 64 | ========== 65 | BaseDriver 66 | ========== 67 | 68 | .. autoclass:: BaseDriver 69 | :members: 70 | 71 | 72 | 73 | 74 | 75 | 76 | -------------------------------------------------------------------------------- /doc/source/contributor/index.rst: -------------------------------------------------------------------------------- 1 | ============================== 2 | Contributing to oslo.messaging 3 | ============================== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | contributing 9 | driver-dev-guide 10 | supported-messaging-drivers 11 | using-simulator 12 | -------------------------------------------------------------------------------- /doc/source/contributor/supported-messaging-drivers.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Supported Messaging Drivers 3 | ============================= 4 | 5 | RabbitMQ may not be sufficient for the entire community as the community 6 | grows. Pluggability is still something we should maintain, but we should 7 | have a very high standard for drivers that are shipped and documented 8 | as being supported. 9 | 10 | This document defines a very clear policy as to the requirements 11 | for drivers to be carried in oslo.messaging and thus supported by the 12 | OpenStack community as a whole. We will deprecate any drivers that do not 13 | meet the requirements, and announce said deprecations in any appropriate 14 | channels to give users time to signal their needs. Deprecation will last 15 | for two release cycles before removing the code. We will also review and 16 | update documentation to annotate which drivers are supported and which 17 | are deprecated given these policies 18 | 19 | Policy 20 | ------ 21 | 22 | Testing 23 | ~~~~~~~ 24 | 25 | * Must have unit and/or functional test coverage of at least 60% as 26 | reported by coverage report. Unit tests must be run for all versions 27 | of python oslo.messaging currently gates on. 28 | 29 | * Must have integration testing including at least 3 popular oslo.messaging 30 | dependents, preferably at the minimum a devstack-gate job with Nova, 31 | Cinder, and Neutron. 32 | 33 | * All testing above must be voting in the gate of oslo.messaging. 34 | 35 | Documentation 36 | ~~~~~~~~~~~~~ 37 | 38 | * Must have a reasonable amount of documentation including documentation 39 | in the official OpenStack deployment guide. 40 | 41 | Support 42 | ~~~~~~~ 43 | 44 | * Must have at least two individuals from the community committed to 45 | triaging and fixing bugs, and responding to test failures in a timely 46 | manner. 47 | 48 | Prospective Drivers 49 | ~~~~~~~~~~~~~~~~~~~ 50 | 51 | * Drivers that intend to meet the requirements above, but that do not yet 52 | meet them will be given one full release cycle, or 6 months, whichever 53 | is longer, to comply before being marked for deprecation. Their use, 54 | however, will not be supported by the community. This will prevent a 55 | chicken and egg problem for new drivers. 56 | 57 | .. note:: 58 | 59 | This work is licensed under a Creative Commons Attribution 3.0 Unported License. 60 | http://creativecommons.org/licenses/by/3.0/legalcode 61 | -------------------------------------------------------------------------------- /doc/source/contributor/using-simulator.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Oslo Messaging Simulator 3 | ============================ 4 | 5 | This guide explains how to set up and run the oslo messaging simulator for testing 6 | different messaging scenarios. 7 | 8 | Prerequisites 9 | ------------- 10 | * Python 3.x 11 | * virtualenv 12 | * wget (for Kafka scenarios) 13 | 14 | Environment Setup 15 | ----------------- 16 | This assumes you have git cloned the oslo.messaging repository and are in the root 17 | directory of the repository. 18 | 19 | Create and activate a virtual environment:: 20 | 21 | python -m venv .venv 22 | source .venv/bin/activate 23 | 24 | Install required packages:: 25 | 26 | pip install pifpaf 27 | pip install -e . 28 | 29 | Running the Simulator 30 | --------------------- 31 | The simulator supports different scenarios for testing messaging patterns. Below 32 | are the common usage patterns. 33 | 34 | Basic Setup 35 | ^^^^^^^^^^^ 36 | Before running the simulator, set up the messaging environment:: 37 | 38 | ./tools/setup-scenario-env.sh 39 | 40 | Available Scenarios 41 | ^^^^^^^^^^^^^^^^^^^ 42 | The simulator supports two main scenarios: 43 | 44 | Scenario 01 (RabbitMQ only) 45 | *************************** 46 | This scenario uses RabbitMQ for both RPC and notifications:: 47 | 48 | export SCENARIO=scenario01 49 | ./tools/setup-scenario-env.sh 50 | 51 | Scenario 02 (RabbitMQ + Kafka) 52 | ****************************** 53 | This scenario uses RabbitMQ for RPC and Kafka for notifications:: 54 | 55 | export SCENARIO=scenario02 56 | ./tools/setup-scenario-env.sh 57 | 58 | Running the Simulator 59 | ^^^^^^^^^^^^^^^^^^^^^ 60 | 61 | RPC Server Example 62 | ****************** 63 | To start the RPC server:: 64 | 65 | python tools/simulator.py --url rabbit://pifpaf:secret@127.0.0.1:5682/ rpc-server 66 | 67 | RPC Client Example 68 | ****************** 69 | To start the RPC client:: 70 | 71 | python tools/simulator.py --url rabbit://pifpaf:secret@127.0.0.1:5682/ rpc-client --exit-wait 15000 -p 64 -m 64 72 | 73 | Optional Configuration 74 | ---------------------- 75 | You can generate a sample configuration file using oslo-config-generator:: 76 | 77 | oslo-config-generator --namespace oslo.messaging > oslo.messaging.conf 78 | 79 | For reference on all available configuration options, visit: 80 | https://docs.openstack.org/oslo.messaging/latest/configuration/opts.html 81 | 82 | To use a configuration file with the simulator, use the --config-file option:: 83 | 84 | python tools/simulator.py --config-file oslo.messaging.conf [other options] 85 | 86 | Command Line Options 87 | -------------------- 88 | The simulator supports various command line options: 89 | 90 | --url URL 91 | The transport URL for the messaging service 92 | --config-file PATH 93 | Path to a configuration file 94 | -d, --debug 95 | Enable debug mode 96 | -p PROCESSES 97 | Number of processes (for client) 98 | -m MESSAGES 99 | Number of messages (for client) 100 | --exit-wait MILLISECONDS 101 | Wait time before exit (for client) 102 | 103 | Cleanup 104 | ------- 105 | To clean up the environment, you can terminate the running processes:: 106 | 107 | pkill -f "RABBITMQ" 108 | 109 | Notes 110 | ----- 111 | * The default scenario is scenario01 if not specified 112 | * Kafka setup is automatic when using scenario02 113 | * The simulator uses pifpaf to manage the message broker processes 114 | * Installing with ``pip install -e .`` allows for development mode installation 115 | * Configuration options can be referenced in the official documentation 116 | -------------------------------------------------------------------------------- /doc/source/index.rst: -------------------------------------------------------------------------------- 1 | ============== 2 | oslo.messaging 3 | ============== 4 | 5 | The Oslo messaging API supports RPC and notifications over a number of 6 | different messaging transports. 7 | 8 | 9 | .. toctree:: 10 | :maxdepth: 1 11 | 12 | contributor/index 13 | configuration/index 14 | admin/index 15 | user/index 16 | reference/index 17 | 18 | 19 | Release Notes 20 | ============= 21 | 22 | Read also the `oslo.messaging Release Notes 23 | `_. 24 | 25 | 26 | Indices and tables 27 | ================== 28 | 29 | * :ref:`genindex` 30 | * :ref:`modindex` 31 | * :ref:`search` 32 | 33 | -------------------------------------------------------------------------------- /doc/source/reference/exceptions.rst: -------------------------------------------------------------------------------- 1 | ---------- 2 | Exceptions 3 | ---------- 4 | 5 | .. currentmodule:: oslo_messaging 6 | 7 | .. autoexception:: ClientSendError 8 | .. autoexception:: DriverLoadFailure 9 | .. autoexception:: ExecutorLoadFailure 10 | .. autoexception:: InvalidTransportURL 11 | .. autoexception:: MessagingException 12 | .. autoexception:: MessagingTimeout 13 | .. autoexception:: MessagingServerError 14 | .. autoexception:: NoSuchMethod 15 | .. autoexception:: RPCDispatcherError 16 | .. autoexception:: RPCVersionCapError 17 | .. autoexception:: ServerListenError 18 | .. autoexception:: UnsupportedVersion 19 | -------------------------------------------------------------------------------- /doc/source/reference/executors.rst: -------------------------------------------------------------------------------- 1 | ========= 2 | Executors 3 | ========= 4 | 5 | Executors control how a received message is scheduled for processing 6 | by a Server. This scheduling can be *synchronous* or *asynchronous*. 7 | 8 | A synchronous executor will process the message on the Server's 9 | thread. This means the Server can process only one message at a time. 10 | Other incoming messages will not be processed until the current 11 | message is done processing. For example, in the case of an RPCServer 12 | only one method call will be invoked at a time. A synchronous 13 | executor guarantees that messages complete processing in the order 14 | that they are received. 15 | 16 | An asynchronous executor will process received messages concurrently. 17 | The Server thread will not be blocked by message processing and can 18 | continue to service incoming messages. There are no ordering 19 | guarantees - message processing may complete in a different order than 20 | they were received. The executor may be configured to limit the 21 | maximum number of messages that are processed at once. 22 | 23 | 24 | Available Executors 25 | =================== 26 | 27 | .. list-plugins:: oslo.messaging.executors 28 | :detailed: 29 | -------------------------------------------------------------------------------- /doc/source/reference/index.rst: -------------------------------------------------------------------------------- 1 | .. _using: 2 | 3 | ========= 4 | Reference 5 | ========= 6 | 7 | .. toctree:: 8 | :maxdepth: 2 9 | 10 | transport 11 | executors 12 | target 13 | server 14 | rpcclient 15 | notifier 16 | notification_driver 17 | notification_listener 18 | serializer 19 | exceptions 20 | -------------------------------------------------------------------------------- /doc/source/reference/notification_driver.rst: -------------------------------------------------------------------------------- 1 | ------------------- 2 | Notification Driver 3 | ------------------- 4 | 5 | .. automodule:: oslo_messaging.notify.messaging 6 | 7 | .. autoclass:: MessagingDriver 8 | 9 | .. autoclass:: MessagingV2Driver 10 | 11 | .. currentmodule:: oslo_messaging.notify.notifier 12 | 13 | .. autoclass:: Driver 14 | :members: 15 | :noindex: 16 | -------------------------------------------------------------------------------- /doc/source/reference/notification_listener.rst: -------------------------------------------------------------------------------- 1 | --------------------- 2 | Notification Listener 3 | --------------------- 4 | 5 | .. automodule:: oslo_messaging.notify.listener 6 | 7 | .. currentmodule:: oslo_messaging 8 | 9 | .. autofunction:: get_notification_listener 10 | 11 | .. autofunction:: get_batch_notification_listener 12 | -------------------------------------------------------------------------------- /doc/source/reference/notifier.rst: -------------------------------------------------------------------------------- 1 | ========== 2 | Notifier 3 | ========== 4 | 5 | .. currentmodule:: oslo_messaging 6 | 7 | .. autoclass:: Notifier 8 | :members: 9 | 10 | .. autoclass:: LoggingNotificationHandler 11 | :members: 12 | 13 | .. autoclass:: LoggingErrorNotificationHandler 14 | :members: 15 | 16 | Available Notifier Drivers 17 | ========================== 18 | 19 | .. list-plugins:: oslo.messaging.notify.drivers 20 | :detailed: 21 | -------------------------------------------------------------------------------- /doc/source/reference/rpcclient.rst: -------------------------------------------------------------------------------- 1 | ---------- 2 | RPC Client 3 | ---------- 4 | 5 | .. currentmodule:: oslo_messaging 6 | 7 | .. autoclass:: RPCClient 8 | :members: 9 | 10 | .. autoexception:: RemoteError 11 | -------------------------------------------------------------------------------- /doc/source/reference/serializer.rst: -------------------------------------------------------------------------------- 1 | ---------- 2 | Serializer 3 | ---------- 4 | 5 | .. currentmodule:: oslo_messaging 6 | 7 | .. autoclass:: Serializer 8 | :members: 9 | 10 | .. autoclass:: NoOpSerializer 11 | -------------------------------------------------------------------------------- /doc/source/reference/server.rst: -------------------------------------------------------------------------------- 1 | ---------- 2 | RPC Server 3 | ---------- 4 | 5 | .. automodule:: oslo_messaging.rpc.server 6 | 7 | .. currentmodule:: oslo_messaging 8 | 9 | .. autofunction:: get_rpc_server 10 | 11 | .. autoclass:: RPCAccessPolicyBase 12 | 13 | .. autoclass:: LegacyRPCAccessPolicy 14 | 15 | .. autoclass:: DefaultRPCAccessPolicy 16 | 17 | .. autoclass:: ExplicitRPCAccessPolicy 18 | 19 | .. autoclass:: RPCDispatcher 20 | 21 | .. autoclass:: MessageHandlingServer 22 | :members: 23 | 24 | .. autofunction:: expected_exceptions 25 | 26 | .. autofunction:: expose 27 | 28 | .. autoexception:: ExpectedException 29 | -------------------------------------------------------------------------------- /doc/source/reference/target.rst: -------------------------------------------------------------------------------- 1 | ------ 2 | Target 3 | ------ 4 | 5 | .. currentmodule:: oslo_messaging 6 | 7 | .. autoclass:: Target 8 | 9 | =============== 10 | Target Versions 11 | =============== 12 | 13 | Target version numbers take the form Major.Minor. For a given message with 14 | version X.Y, the server must be marked as able to handle messages of version 15 | A.B, where A == X and B >= Y. 16 | 17 | The Major version number should be incremented for an almost completely new 18 | API. The Minor version number would be incremented for backwards compatible 19 | changes to an existing API. A backwards compatible change could be something 20 | like adding a new method, adding an argument to an existing method (but not 21 | requiring it), or changing the type for an existing argument (but still 22 | handling the old type as well). 23 | 24 | If no version is specified it defaults to '1.0'. 25 | 26 | In the case of RPC, if you wish to allow your server interfaces to evolve such 27 | that clients do not need to be updated in lockstep with the server, you should 28 | take care to implement the server changes in a backwards compatible and have 29 | the clients specify which interface version they require for each method. 30 | 31 | Adding a new method to an endpoint is a backwards compatible change and the 32 | version attribute of the endpoint's target should be bumped from X.Y to X.Y+1. 33 | On the client side, the new RPC invocation should have a specific version 34 | specified to indicate the minimum API version that must be implemented for the 35 | method to be supported. For example:: 36 | 37 | def get_host_uptime(self, ctxt, host): 38 | cctxt = self.client.prepare(server=host, version='1.1') 39 | return cctxt.call(ctxt, 'get_host_uptime') 40 | 41 | In this case, version '1.1' is the first version that supported the 42 | get_host_uptime() method. 43 | 44 | Adding a new parameter to an RPC method can be made backwards compatible. The 45 | endpoint version on the server side should be bumped. The implementation of 46 | the method must not expect the parameter to be present.:: 47 | 48 | def some_remote_method(self, arg1, arg2, newarg=None): 49 | # The code needs to deal with newarg=None for cases 50 | # where an older client sends a message without it. 51 | pass 52 | 53 | On the client side, the same changes should be made as in example 1. The 54 | minimum version that supports the new parameter should be specified. 55 | -------------------------------------------------------------------------------- /doc/source/reference/transport.rst: -------------------------------------------------------------------------------- 1 | --------- 2 | Transport 3 | --------- 4 | 5 | .. currentmodule:: oslo_messaging 6 | 7 | .. autoclass:: Transport 8 | 9 | .. autoclass:: TransportURL 10 | :members: 11 | 12 | .. autoclass:: TransportHost 13 | 14 | .. autofunction:: set_transport_defaults 15 | 16 | 17 | Forking Processes and oslo.messaging Transport objects 18 | ------------------------------------------------------ 19 | 20 | oslo.messaging can't ensure that forking a process that shares the same 21 | transport object is safe for the library consumer, because it relies on 22 | different 3rd party libraries that don't ensure that. In certain 23 | cases, with some drivers, it does work: 24 | 25 | * rabbit: works only if no connection have already been established. 26 | -------------------------------------------------------------------------------- /doc/source/user/FAQ.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Frequently Asked Questions 3 | ============================ 4 | 5 | I don't need notifications on the message bus. How do I disable them? 6 | ===================================================================== 7 | 8 | Notification messages can be disabled using the ``noop`` notify 9 | driver. Set ``driver = noop`` in your configuration file under the 10 | [oslo_messaging_notifications] section. 11 | 12 | Why does the notification publisher create queues, too? Shouldn't the subscriber do that? 13 | ========================================================================================= 14 | 15 | The notification messages are meant to be used for integration with 16 | external services, including services that are not part of 17 | OpenStack. To ensure that the subscriber does not miss any messages if 18 | it starts after the publisher, ``oslo.messaging`` ensures that 19 | subscriber queues exist when notifications are sent. 20 | 21 | How do I change the queue names where notifications are published? 22 | ================================================================== 23 | 24 | Notifications are published to the configured exchange using a topic 25 | built from a base value specified in the configuration file and the 26 | notification "level". The default topic is ``notifications``, so an 27 | info-level notification is published to the topic 28 | ``notifications.info``. A subscriber queue of the same name is created 29 | automatically for each of these topics. To change the queue names, 30 | change the notification topic using the ``topics`` 31 | configuration option in ``[oslo_messaging_notifications]``. The option 32 | accepts a list of values, so it is possible to publish to multiple topics. 33 | 34 | What are the other choices of notification drivers available? 35 | ============================================================= 36 | 37 | - messaging Send notifications using the 1.0 message format. 38 | - messagingv2 Send notifications using the 2.0 message format (with a message envelope). 39 | - routing Configurable routing notifier (by priority or event_type). 40 | - log Publish notifications via Python logging infrastructure. 41 | - test Store notifications in memory for test verification. 42 | - noop Disable sending notifications entirely. 43 | -------------------------------------------------------------------------------- /doc/source/user/history.rst: -------------------------------------------------------------------------------- 1 | .. include:: ../../../ChangeLog 2 | -------------------------------------------------------------------------------- /doc/source/user/index.rst: -------------------------------------------------------------------------------- 1 | ==================== 2 | Using oslo.messaging 3 | ==================== 4 | 5 | .. toctree:: 6 | :maxdepth: 2 7 | 8 | FAQ 9 | 10 | .. toctree:: 11 | :maxdepth: 1 12 | 13 | history 14 | -------------------------------------------------------------------------------- /etc/routing_notifier.yaml.sample: -------------------------------------------------------------------------------- 1 | # Setting a priority AND an event means both have to be satisfied. 2 | # 3 | # However, defining different sets for the same driver allows you 4 | # to do OR operations. 5 | # 6 | # See how this logic is modelled below: 7 | # 8 | # if (priority in info, warn or error) or 9 | # (event == compute.scheduler.run_instance) 10 | # send to messaging driver ... 11 | # 12 | # if priority == 'poll' and 13 | # event == 'bandwidth.*' 14 | # send to poll driver 15 | 16 | group_1: 17 | messaging: 18 | accepted_priorities: ['info', 'warn', 'error'] 19 | 20 | poll: 21 | accepted_priorities: ['poll'] 22 | accepted_events: ['bandwidth.*'] 23 | 24 | log: 25 | accepted_events: ['compute.instance.exists'] 26 | 27 | group_2: 28 | messaging:⋅ 29 | accepted_events: ['compute.scheduler.run_instance.*'] 30 | -------------------------------------------------------------------------------- /oslo_messaging/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from .exceptions import * 16 | from .notify import * 17 | from .rpc import * 18 | from .serializer import * 19 | from .server import * 20 | from .target import * 21 | from .transport import * 22 | -------------------------------------------------------------------------------- /oslo_messaging/_drivers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/oslo_messaging/_drivers/__init__.py -------------------------------------------------------------------------------- /oslo_messaging/_drivers/amqp.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010 United States Government as represented by the 2 | # Administrator of the National Aeronautics and Space Administration. 3 | # All Rights Reserved. 4 | # Copyright 2011 - 2012, Red Hat, Inc. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 7 | # not use this file except in compliance with the License. You may obtain 8 | # a copy of the License at 9 | # 10 | # http://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15 | # License for the specific language governing permissions and limitations 16 | # under the License. 17 | 18 | """ 19 | Utilities for drivers based on the AMQPDriverBase. 20 | 21 | This module contains utility code used by drivers based on the AMQPDriverBase 22 | class. Specifically this includes the impl_rabbit driver. 23 | """ 24 | 25 | import collections 26 | import uuid 27 | 28 | from oslo_config import cfg 29 | 30 | from oslo_messaging._drivers import common as rpc_common 31 | 32 | amqp_opts = [ 33 | cfg.BoolOpt('amqp_durable_queues', 34 | default=False, 35 | help='Use durable queues in AMQP. If rabbit_quorum_queue ' 36 | 'is enabled, queues will be durable and this value will ' 37 | 'be ignored.'), 38 | cfg.BoolOpt('amqp_auto_delete', 39 | default=False, 40 | help='Auto-delete queues in AMQP.'), 41 | ] 42 | 43 | UNIQUE_ID = '_unique_id' 44 | 45 | 46 | class RpcContext(rpc_common.CommonRpcContext): 47 | """Context that supports replying to a rpc.call.""" 48 | def __init__(self, **kwargs): 49 | self.msg_id = kwargs.pop('msg_id', None) 50 | self.reply_q = kwargs.pop('reply_q', None) 51 | super().__init__(**kwargs) 52 | 53 | def deepcopy(self): 54 | values = self.to_dict() 55 | values['conf'] = self.conf 56 | values['msg_id'] = self.msg_id 57 | values['reply_q'] = self.reply_q 58 | return self.__class__(**values) 59 | 60 | 61 | def unpack_context(msg): 62 | """Unpack context from msg.""" 63 | context_dict = {} 64 | for key in list(msg.keys()): 65 | key = str(key) 66 | if key.startswith('_context_'): 67 | value = msg.pop(key) 68 | context_dict[key[9:]] = value 69 | context_dict['msg_id'] = msg.pop('_msg_id', None) 70 | context_dict['reply_q'] = msg.pop('_reply_q', None) 71 | context_dict['client_timeout'] = msg.pop('_timeout', None) 72 | return RpcContext.from_dict(context_dict) 73 | 74 | 75 | def pack_context(msg, context): 76 | """Pack context into msg. 77 | 78 | Values for message keys need to be less than 255 chars, so we pull 79 | context out into a bunch of separate keys. If we want to support 80 | more arguments in rabbit messages, we may want to do the same 81 | for args at some point. 82 | 83 | """ 84 | if isinstance(context, dict): 85 | context_d = context.items() 86 | else: 87 | context_d = context.to_dict().items() 88 | 89 | msg.update(('_context_%s' % key, value) 90 | for (key, value) in context_d) 91 | 92 | 93 | class _MsgIdCache: 94 | """This class checks any duplicate messages.""" 95 | 96 | # NOTE: This value is considered can be a configuration item, but 97 | # it is not necessary to change its value in most cases, 98 | # so let this value as static for now. 99 | DUP_MSG_CHECK_SIZE = 16 100 | 101 | def __init__(self, **kwargs): 102 | self.prev_msgids = collections.deque([], 103 | maxlen=self.DUP_MSG_CHECK_SIZE) 104 | 105 | def check_duplicate_message(self, message_data): 106 | """AMQP consumers may read same message twice when exceptions occur 107 | before ack is returned. This method prevents doing it. 108 | """ 109 | try: 110 | msg_id = message_data.pop(UNIQUE_ID) 111 | except KeyError: 112 | return 113 | if msg_id in self.prev_msgids: 114 | raise rpc_common.DuplicateMessageError(msg_id=msg_id) 115 | return msg_id 116 | 117 | def add(self, msg_id): 118 | if msg_id and msg_id not in self.prev_msgids: 119 | self.prev_msgids.append(msg_id) 120 | 121 | 122 | def _add_unique_id(msg): 123 | """Add unique_id for checking duplicate messages.""" 124 | unique_id = uuid.uuid4().hex 125 | msg.update({UNIQUE_ID: unique_id}) 126 | 127 | 128 | class AMQPDestinationNotFound(Exception): 129 | pass 130 | -------------------------------------------------------------------------------- /oslo_messaging/_drivers/kafka_driver/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/oslo_messaging/_drivers/kafka_driver/__init__.py -------------------------------------------------------------------------------- /oslo_messaging/_drivers/kafka_driver/kafka_options.py: -------------------------------------------------------------------------------- 1 | # 2 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 3 | # not use this file except in compliance with the License. You may obtain 4 | # a copy of the License at 5 | # 6 | # http://www.apache.org/licenses/LICENSE-2.0 7 | # 8 | # Unless required by applicable law or agreed to in writing, software 9 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 10 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 11 | # License for the specific language governing permissions and limitations 12 | # under the License. 13 | 14 | from oslo_config import cfg 15 | 16 | from oslo_messaging._drivers import common 17 | 18 | KAFKA_OPTS = [ 19 | cfg.IntOpt('kafka_max_fetch_bytes', default=1024 * 1024, 20 | help='Max fetch bytes of Kafka consumer'), 21 | cfg.FloatOpt('kafka_consumer_timeout', default=1.0, 22 | help='Default timeout(s) for Kafka consumers'), 23 | cfg.StrOpt('consumer_group', default="oslo_messaging_consumer", 24 | help='Group id for Kafka consumer. Consumers in one group ' 25 | 'will coordinate message consumption'), 26 | cfg.FloatOpt('producer_batch_timeout', default=0., 27 | help="Upper bound on the delay for KafkaProducer batching " 28 | "in seconds"), 29 | cfg.IntOpt('producer_batch_size', default=16384, 30 | help='Size of batch for the producer async send'), 31 | cfg.StrOpt('compression_codec', default='none', 32 | choices=['none', 'gzip', 'snappy', 'lz4', 'zstd'], 33 | help='The compression codec for all data generated by the ' 34 | 'producer. If not set, compression will not be used. ' 35 | 'Note that the allowed values of this depend on the kafka ' 36 | 'version'), 37 | cfg.BoolOpt('enable_auto_commit', 38 | default=False, 39 | help='Enable asynchronous consumer commits'), 40 | cfg.IntOpt('max_poll_records', default=500, 41 | help='The maximum number of records returned in a poll call'), 42 | cfg.StrOpt('security_protocol', default='PLAINTEXT', 43 | choices=('PLAINTEXT', 'SASL_PLAINTEXT', 'SSL', 'SASL_SSL'), 44 | help='Protocol used to communicate with brokers'), 45 | cfg.StrOpt('sasl_mechanism', 46 | default='PLAIN', 47 | help='Mechanism when security protocol is SASL'), 48 | cfg.StrOpt('ssl_cafile', 49 | default='', 50 | help='CA certificate PEM file used to verify the server' 51 | ' certificate'), 52 | cfg.StrOpt('ssl_client_cert_file', 53 | default='', 54 | help='Client certificate PEM file used for authentication.'), 55 | cfg.StrOpt('ssl_client_key_file', 56 | default='', 57 | help='Client key PEM file used for authentication.'), 58 | cfg.StrOpt('ssl_client_key_password', 59 | default='', 60 | help='Client key password file used for authentication.') 61 | ] 62 | 63 | 64 | def register_opts(conf, url): 65 | opt_group = cfg.OptGroup(name='oslo_messaging_kafka', 66 | title='Kafka driver options') 67 | conf.register_group(opt_group) 68 | conf.register_opts(KAFKA_OPTS, group=opt_group) 69 | return common.ConfigOptsProxy(conf, url, opt_group.name) 70 | -------------------------------------------------------------------------------- /oslo_messaging/_drivers/pool.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import abc 16 | import collections 17 | import threading 18 | 19 | from oslo_log import log as logging 20 | from oslo_utils import timeutils 21 | 22 | from oslo_messaging._drivers import common 23 | 24 | LOG = logging.getLogger(__name__) 25 | 26 | 27 | class Pool(metaclass=abc.ABCMeta): 28 | """A thread-safe object pool. 29 | 30 | Modelled after the eventlet.pools.Pool interface, but designed to be safe 31 | when using native threads without the GIL. 32 | 33 | Resizing is not supported. 34 | 35 | """ 36 | 37 | def __init__(self, max_size=4, min_size=2, ttl=1200, on_expire=None): 38 | super().__init__() 39 | self._min_size = min_size 40 | self._max_size = max_size 41 | self._item_ttl = ttl 42 | self._current_size = 0 43 | self._cond = threading.Condition() 44 | self._items = collections.deque() 45 | self._on_expire = on_expire 46 | 47 | def expire(self): 48 | """Remove expired items from left (the oldest item) to 49 | right (the newest item). 50 | """ 51 | with self._cond: 52 | while len(self._items) > self._min_size: 53 | try: 54 | ttl_watch, item = self._items.popleft() 55 | if ttl_watch.expired(): 56 | self._on_expire and self._on_expire(item) 57 | self._current_size -= 1 58 | else: 59 | self._items.appendleft((ttl_watch, item)) 60 | return 61 | except IndexError: 62 | break 63 | 64 | def put(self, item): 65 | """Return an item to the pool.""" 66 | with self._cond: 67 | ttl_watch = timeutils.StopWatch(duration=self._item_ttl) 68 | ttl_watch.start() 69 | self._items.append((ttl_watch, item)) 70 | self._cond.notify() 71 | 72 | def get(self, retry=None): 73 | """Return an item from the pool, when one is available. 74 | 75 | This may cause the calling thread to block. 76 | """ 77 | with self._cond: 78 | while True: 79 | try: 80 | ttl_watch, item = self._items.pop() 81 | self.expire() 82 | return item 83 | except IndexError: 84 | pass 85 | 86 | if self._current_size < self._max_size: 87 | self._current_size += 1 88 | break 89 | 90 | LOG.warning("Connection pool limit exceeded: " 91 | "current size %s surpasses max " 92 | "configured rpc_conn_pool_size %s", 93 | self._current_size, self._max_size) 94 | self._cond.wait() 95 | 96 | # We've grabbed a slot and dropped the lock, now do the creation 97 | try: 98 | return self.create(retry=retry) 99 | except Exception: 100 | with self._cond: 101 | self._current_size -= 1 102 | raise 103 | 104 | def iter_free(self): 105 | """Iterate over free items.""" 106 | while True: 107 | try: 108 | _, item = self._items.pop() 109 | yield item 110 | except IndexError: 111 | return 112 | 113 | @abc.abstractmethod 114 | def create(self, retry=None): 115 | """Construct a new item.""" 116 | 117 | 118 | class ConnectionPool(Pool): 119 | """Class that implements a Pool of Connections.""" 120 | 121 | def __init__(self, conf, max_size, min_size, ttl, url, connection_cls): 122 | self.connection_cls = connection_cls 123 | self.conf = conf 124 | self.url = url 125 | super().__init__(max_size, min_size, ttl, 126 | self._on_expire) 127 | 128 | def _on_expire(self, connection): 129 | connection.close() 130 | LOG.debug("Idle connection has expired and been closed." 131 | " Pool size: %d" % len(self._items)) 132 | 133 | def create(self, purpose=common.PURPOSE_SEND, retry=None): 134 | LOG.debug('Pool creating new connection') 135 | return self.connection_cls(self.conf, self.url, purpose, retry=retry) 136 | 137 | def empty(self): 138 | for item in self.iter_free(): 139 | item.close() 140 | -------------------------------------------------------------------------------- /oslo_messaging/_metrics/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2020 LINE Corp. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the 'License'); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an 'AS IS' BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | __all__ = [ 15 | 'MetricsCollectorClient', 16 | 'get_collector', 17 | 'measure_metrics', 18 | ] 19 | 20 | from .client import * 21 | -------------------------------------------------------------------------------- /oslo_messaging/_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import logging 16 | import queue 17 | import threading 18 | 19 | from oslo_utils import eventletutils 20 | from oslo_utils import importutils 21 | 22 | LOG = logging.getLogger(__name__) 23 | 24 | eventlet = importutils.try_import('eventlet') 25 | if eventlet and eventletutils.is_monkey_patched("thread"): 26 | # Here we initialize module with the native python threading module 27 | # if it was already monkey patched by eventlet/greenlet. 28 | stdlib_threading = eventlet.patcher.original('threading') 29 | stdlib_queue = eventlet.patcher.original('queue') 30 | else: 31 | # Manage the case where we run this driver in a non patched environment 32 | # and where user even so configure the driver to run heartbeat through 33 | # a python thread, if we don't do that when the heartbeat will start 34 | # we will facing an issue by trying to override the threading module. 35 | stdlib_threading = threading 36 | stdlib_queue = queue 37 | 38 | 39 | def version_is_compatible(imp_version, version): 40 | """Determine whether versions are compatible. 41 | 42 | :param imp_version: The version implemented 43 | :param version: The version requested by an incoming message. 44 | """ 45 | if imp_version is None: 46 | return True 47 | 48 | if version is None: 49 | return False 50 | 51 | version_parts = version.split('.') 52 | imp_version_parts = imp_version.split('.') 53 | try: 54 | rev = version_parts[2] 55 | except IndexError: 56 | rev = 0 57 | try: 58 | imp_rev = imp_version_parts[2] 59 | except IndexError: 60 | imp_rev = 0 61 | 62 | if int(version_parts[0]) != int(imp_version_parts[0]): # Major 63 | return False 64 | if int(version_parts[1]) > int(imp_version_parts[1]): # Minor 65 | return False 66 | if (int(version_parts[1]) == int(imp_version_parts[1]) and 67 | int(rev) > int(imp_rev)): # Revision 68 | return False 69 | return True 70 | 71 | 72 | class DummyLock: 73 | def acquire(self): 74 | pass 75 | 76 | def release(self): 77 | pass 78 | 79 | def __enter__(self): 80 | self.acquire() 81 | 82 | def __exit__(self, type, value, traceback): 83 | self.release() 84 | 85 | 86 | def get_executor_with_context(): 87 | if eventletutils.is_monkey_patched('thread'): 88 | LOG.debug("Threading is patched, using an eventlet executor") 89 | return 'eventlet' 90 | LOG.debug("Using a threading executor") 91 | return 'threading' 92 | -------------------------------------------------------------------------------- /oslo_messaging/conffixture.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import sys 16 | 17 | import fixtures 18 | from functools import wraps 19 | 20 | __all__ = ['ConfFixture'] 21 | 22 | 23 | def _import_opts(conf, module, opts, group=None): 24 | __import__(module) 25 | conf.register_opts(getattr(sys.modules[module], opts), group=group) 26 | 27 | 28 | class ConfFixture(fixtures.Fixture): 29 | 30 | """Tweak configuration options for unit testing. 31 | 32 | oslo.messaging registers a number of configuration options, but rather than 33 | directly referencing those options, users of the API should use this 34 | interface for querying and overriding certain configuration options. 35 | 36 | An example usage:: 37 | 38 | self.messaging_conf = self.useFixture(messaging.ConfFixture(cfg.CONF)) 39 | self.messaging_conf.transport_url = 'fake:/' 40 | 41 | :param conf: a ConfigOpts instance 42 | :type conf: oslo.config.cfg.ConfigOpts 43 | :param transport_url: override default transport_url value 44 | :type transport_url: str 45 | """ 46 | 47 | def __init__(self, conf, transport_url=None): 48 | self.conf = conf 49 | _import_opts(self.conf, 50 | 'oslo_messaging._drivers.impl_rabbit', 'rabbit_opts', 51 | 'oslo_messaging_rabbit') 52 | _import_opts(self.conf, 53 | 'oslo_messaging._drivers.amqp', 'amqp_opts', 54 | 'oslo_messaging_rabbit') 55 | _import_opts(self.conf, 'oslo_messaging.rpc.client', '_client_opts') 56 | _import_opts(self.conf, 'oslo_messaging.transport', '_transport_opts') 57 | _import_opts(self.conf, 'oslo_messaging.rpc.dispatcher', 58 | '_dispatcher_opts') 59 | _import_opts(self.conf, 60 | 'oslo_messaging.notify.notifier', 61 | '_notifier_opts', 62 | 'oslo_messaging_notifications') 63 | _import_opts(self.conf, 64 | 'oslo_messaging._metrics.client', 65 | 'oslo_messaging_metrics', 66 | 'oslo_messaging_metrics') 67 | 68 | if transport_url is not None: 69 | self.transport_url = transport_url 70 | 71 | def _setup_decorator(self): 72 | # Support older test cases that still use the set_override 73 | # with the old config key names 74 | def decorator_for_set_override(wrapped_function): 75 | @wraps(wrapped_function) 76 | def _wrapper(*args, **kwargs): 77 | group = 'oslo_messaging_notifications' 78 | if args[0] == 'notification_driver': 79 | args = ('driver', args[1], group) 80 | elif args[0] == 'notification_transport_url': 81 | args = ('transport_url', args[1], group) 82 | elif args[0] == 'notification_topics': 83 | args = ('topics', args[1], group) 84 | return wrapped_function(*args, **kwargs) 85 | _wrapper.wrapped = wrapped_function 86 | return _wrapper 87 | 88 | def decorator_for_clear_override(wrapped_function): 89 | @wraps(wrapped_function) 90 | def _wrapper(*args, **kwargs): 91 | group = 'oslo_messaging_notifications' 92 | if args[0] == 'notification_driver': 93 | args = ('driver', group) 94 | elif args[0] == 'notification_transport_url': 95 | args = ('transport_url', group) 96 | elif args[0] == 'notification_topics': 97 | args = ('topics', group) 98 | return wrapped_function(*args, **kwargs) 99 | _wrapper.wrapped = wrapped_function 100 | return _wrapper 101 | 102 | if not hasattr(self.conf.set_override, 'wrapped'): 103 | self.conf.set_override = decorator_for_set_override( 104 | self.conf.set_override) 105 | if not hasattr(self.conf.clear_override, 'wrapped'): 106 | self.conf.clear_override = decorator_for_clear_override( 107 | self.conf.clear_override) 108 | 109 | def _teardown_decorator(self): 110 | if hasattr(self.conf.set_override, 'wrapped'): 111 | self.conf.set_override = self.conf.set_override.wrapped 112 | if hasattr(self.conf.clear_override, 'wrapped'): 113 | self.conf.clear_override = self.conf.clear_override.wrapped 114 | 115 | def setUp(self): 116 | super().setUp() 117 | self._setup_decorator() 118 | self.addCleanup(self._teardown_decorator) 119 | self.addCleanup(self.conf.reset) 120 | 121 | @property 122 | def transport_url(self): 123 | """The transport url""" 124 | return self.conf.transport_url 125 | 126 | @transport_url.setter 127 | def transport_url(self, value): 128 | self.conf.set_override('transport_url', value) 129 | 130 | @property 131 | def response_timeout(self): 132 | """Default number of seconds to wait for a response from a call.""" 133 | return self.conf.rpc_response_timeout 134 | 135 | @response_timeout.setter 136 | def response_timeout(self, value): 137 | self.conf.set_override('rpc_response_timeout', value) 138 | -------------------------------------------------------------------------------- /oslo_messaging/dispatcher.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import abc 14 | 15 | 16 | __all__ = [ 17 | "DispatcherBase" 18 | ] 19 | 20 | 21 | class DispatcherBase(metaclass=abc.ABCMeta): 22 | "Base class for dispatcher" 23 | 24 | @abc.abstractmethod 25 | def dispatch(self, incoming): 26 | """Dispatch incoming messages to the endpoints and return result 27 | 28 | :param incoming: incoming object for dispatching to the endpoint 29 | :type incoming: object, depends on endpoint type 30 | """ 31 | -------------------------------------------------------------------------------- /oslo_messaging/exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | __all__ = ['MessagingException', 'MessagingTimeout', 'MessageDeliveryFailure', 16 | 'InvalidTarget', 'MessageUndeliverable'] 17 | 18 | 19 | class MessagingException(Exception): 20 | """Base class for exceptions.""" 21 | 22 | 23 | class MessagingTimeout(MessagingException): 24 | """Raised if message sending times out.""" 25 | 26 | 27 | class MessageDeliveryFailure(MessagingException): 28 | """Raised if message sending failed after the asked retry.""" 29 | 30 | 31 | class InvalidTarget(MessagingException, ValueError): 32 | """Raised if a target does not meet certain pre-conditions.""" 33 | 34 | def __init__(self, msg, target): 35 | msg = msg + ":" + str(target) 36 | super().__init__(msg) 37 | self.target = target 38 | 39 | 40 | class MessageUndeliverable(Exception): 41 | """Raised if message is not routed with mandatory flag""" 42 | 43 | def __init__(self, exception, exchange, routing_key, message): 44 | super().__init__() 45 | self.exception = exception 46 | self.exchange = exchange 47 | self.routing_key = routing_key 48 | self.message = message 49 | 50 | 51 | class ConfigurationError(Exception): 52 | """Raised when messaging isn't configured correctly.""" 53 | -------------------------------------------------------------------------------- /oslo_messaging/hacking/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/oslo_messaging/hacking/__init__.py -------------------------------------------------------------------------------- /oslo_messaging/notify/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | __all__ = ['Notifier', 16 | 'LoggingNotificationHandler', 17 | 'get_notification_transport', 18 | 'get_notification_listener', 19 | 'get_batch_notification_listener', 20 | 'NotificationResult', 21 | 'NotificationFilter', 22 | 'PublishErrorsHandler', 23 | 'LoggingErrorNotificationHandler'] 24 | 25 | from .filter import NotificationFilter 26 | from .notifier import * 27 | from .listener import * 28 | from .log_handler import * 29 | from .logger import * 30 | from .dispatcher import NotificationResult 31 | -------------------------------------------------------------------------------- /oslo_messaging/notify/_impl_log.py: -------------------------------------------------------------------------------- 1 | # Copyright 2011 OpenStack Foundation. 2 | # All Rights Reserved. 3 | # Copyright 2013 Red Hat, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import logging 18 | import warnings 19 | 20 | from oslo_serialization import jsonutils 21 | from oslo_utils import strutils 22 | 23 | from oslo_messaging.notify import notifier 24 | 25 | 26 | class LogDriver(notifier.Driver): 27 | 28 | "Publish notifications via Python logging infrastructure." 29 | 30 | # NOTE(dhellmann): For backwards-compatibility with configurations 31 | # that may have modified the settings for this logger using a 32 | # configuration file, we keep the name 33 | # 'oslo.messaging.notification' even though the package is now 34 | # 'oslo_messaging'. 35 | LOGGER_BASE = 'oslo.messaging.notification' 36 | 37 | def notify(self, ctxt, message, priority, retry): 38 | logger = logging.getLogger('{}.{}'.format(self.LOGGER_BASE, 39 | message['event_type'])) 40 | method = getattr(logger, priority.lower(), None) 41 | if method: 42 | method(jsonutils.dumps(strutils.mask_dict_password(message))) 43 | else: 44 | warnings.warn('Unable to log message as notify cannot find a ' 45 | 'logger with the priority specified ' 46 | '%s' % priority.lower()) 47 | -------------------------------------------------------------------------------- /oslo_messaging/notify/_impl_noop.py: -------------------------------------------------------------------------------- 1 | # Copyright 2011 OpenStack Foundation. 2 | # All Rights Reserved. 3 | # Copyright 2013 Red Hat, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from oslo_messaging.notify import notifier 18 | 19 | 20 | class NoOpDriver(notifier.Driver): 21 | 22 | def notify(self, ctxt, message, priority, retry): 23 | pass 24 | -------------------------------------------------------------------------------- /oslo_messaging/notify/_impl_routing.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Rackspace Hosting 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import fnmatch 17 | import logging 18 | 19 | from oslo_config import cfg 20 | from stevedore import dispatch 21 | import yaml 22 | 23 | from oslo_messaging.notify import notifier 24 | 25 | 26 | LOG = logging.getLogger(__name__) 27 | 28 | router_config = cfg.StrOpt('routing_config', default='', 29 | help='RoutingNotifier configuration file location.') 30 | 31 | CONF = cfg.CONF 32 | CONF.register_opt(router_config, group='oslo_messaging_notifications') 33 | 34 | 35 | class RoutingDriver(notifier.Driver): 36 | NOTIFIER_PLUGIN_NAMESPACE = 'oslo.messaging.notify.drivers' 37 | 38 | plugin_manager = None 39 | routing_groups = None # The routing groups from the config file. 40 | used_drivers = None # Used driver names, extracted from config file. 41 | 42 | def _should_load_plugin(self, ext, *args, **kwargs): 43 | # Hack to keep stevedore from circular importing since these 44 | # endpoints are used for different purposes. 45 | if ext.name == 'routing': 46 | return False 47 | return ext.name in self.used_drivers 48 | 49 | def _get_notifier_config_file(self, filename): 50 | """Broken out for testing.""" 51 | return open(filename) 52 | 53 | def _load_notifiers(self): 54 | """One-time load of notifier config file.""" 55 | self.routing_groups = {} 56 | self.used_drivers = set() 57 | filename = CONF.oslo_messaging_notifications.routing_config 58 | if not filename: 59 | return 60 | 61 | # Infer which drivers are used from the config file. 62 | self.routing_groups = yaml.safe_load( 63 | self._get_notifier_config_file(filename)) 64 | if not self.routing_groups: 65 | self.routing_groups = {} # In case we got None from load() 66 | return 67 | 68 | for group in self.routing_groups.values(): 69 | self.used_drivers.update(group.keys()) 70 | 71 | LOG.debug('loading notifiers from %s', self.NOTIFIER_PLUGIN_NAMESPACE) 72 | self.plugin_manager = dispatch.DispatchExtensionManager( 73 | namespace=self.NOTIFIER_PLUGIN_NAMESPACE, 74 | check_func=self._should_load_plugin, 75 | invoke_on_load=True, 76 | invoke_args=None) 77 | if not list(self.plugin_manager): 78 | LOG.warning("Failed to load any notifiers for %s", 79 | self.NOTIFIER_PLUGIN_NAMESPACE) 80 | 81 | def _get_drivers_for_message(self, group, event_type, priority): 82 | """Which drivers should be called for this event_type 83 | or priority. 84 | """ 85 | accepted_drivers = set() 86 | 87 | for driver, rules in group.items(): 88 | checks = [] 89 | for key, patterns in rules.items(): 90 | if key == 'accepted_events': 91 | c = [fnmatch.fnmatch(event_type, p) 92 | for p in patterns] 93 | checks.append(any(c)) 94 | if key == 'accepted_priorities': 95 | c = [fnmatch.fnmatch(priority, p.lower()) 96 | for p in patterns] 97 | checks.append(any(c)) 98 | if all(checks): 99 | accepted_drivers.add(driver) 100 | 101 | return list(accepted_drivers) 102 | 103 | def _filter_func(self, ext, context, message, priority, retry, 104 | accepted_drivers): 105 | """True/False if the driver should be called for this message. 106 | """ 107 | # context is unused here, but passed in by map() 108 | return ext.name in accepted_drivers 109 | 110 | def _call_notify(self, ext, context, message, priority, retry, 111 | accepted_drivers): 112 | """Emit the notification. 113 | """ 114 | # accepted_drivers is passed in as a result of the map() function 115 | LOG.info("Routing '%(event)s' notification to '%(driver)s' " 116 | "driver", 117 | {'event': message.get('event_type'), 'driver': ext.name}) 118 | ext.obj.notify(context, message, priority, retry) 119 | 120 | def notify(self, context, message, priority, retry): 121 | if not self.plugin_manager: 122 | self._load_notifiers() 123 | 124 | # Fail if these aren't present ... 125 | event_type = message['event_type'] 126 | 127 | accepted_drivers = set() 128 | for group in self.routing_groups.values(): 129 | accepted_drivers.update( 130 | self._get_drivers_for_message(group, event_type, 131 | priority.lower())) 132 | self.plugin_manager.map(self._filter_func, self._call_notify, context, 133 | message, priority, retry, 134 | list(accepted_drivers)) 135 | -------------------------------------------------------------------------------- /oslo_messaging/notify/_impl_test.py: -------------------------------------------------------------------------------- 1 | # Copyright 2011 OpenStack Foundation. 2 | # All Rights Reserved. 3 | # Copyright 2013 Red Hat, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from oslo_messaging.notify import notifier 18 | 19 | NOTIFICATIONS = [] 20 | 21 | 22 | def reset(): 23 | "Clear out the list of recorded notifications." 24 | global NOTIFICATIONS 25 | NOTIFICATIONS = [] 26 | 27 | 28 | class TestDriver(notifier.Driver): 29 | 30 | "Store notifications in memory for test verification." 31 | 32 | def notify(self, ctxt, message, priority, retry): 33 | NOTIFICATIONS.append((ctxt, message, priority, retry)) 34 | -------------------------------------------------------------------------------- /oslo_messaging/notify/dispatcher.py: -------------------------------------------------------------------------------- 1 | # Copyright 2011 OpenStack Foundation. 2 | # All Rights Reserved. 3 | # Copyright 2013 eNovance 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | import itertools 18 | import logging 19 | import operator 20 | 21 | from oslo_messaging import dispatcher 22 | from oslo_messaging import serializer as msg_serializer 23 | 24 | 25 | LOG = logging.getLogger(__name__) 26 | 27 | PRIORITIES = ['audit', 'debug', 'info', 'warn', 'error', 'critical', 'sample'] 28 | 29 | 30 | class NotificationResult: 31 | HANDLED = 'handled' 32 | REQUEUE = 'requeue' 33 | 34 | 35 | class NotificationDispatcher(dispatcher.DispatcherBase): 36 | def __init__(self, endpoints, serializer): 37 | 38 | self.endpoints = endpoints 39 | self.serializer = serializer or msg_serializer.NoOpSerializer() 40 | 41 | self._callbacks_by_priority = {} 42 | for endpoint, prio in itertools.product(endpoints, PRIORITIES): 43 | if hasattr(endpoint, prio): 44 | method = getattr(endpoint, prio) 45 | screen = getattr(endpoint, 'filter_rule', None) 46 | self._callbacks_by_priority.setdefault(prio, []).append( 47 | (screen, method)) 48 | 49 | @property 50 | def supported_priorities(self): 51 | return self._callbacks_by_priority.keys() 52 | 53 | def dispatch(self, incoming): 54 | """Dispatch notification messages to the appropriate endpoint method. 55 | """ 56 | priority, raw_message, message = self._extract_user_message(incoming) 57 | 58 | if priority not in PRIORITIES: 59 | LOG.warning('Unknown priority "%s"', priority) 60 | return 61 | 62 | for screen, callback in self._callbacks_by_priority.get(priority, 63 | []): 64 | if screen and not screen.match(message["ctxt"], 65 | message["publisher_id"], 66 | message["event_type"], 67 | message["metadata"], 68 | message["payload"]): 69 | continue 70 | 71 | ret = self._exec_callback(callback, message) 72 | if ret == NotificationResult.REQUEUE: 73 | return ret 74 | return NotificationResult.HANDLED 75 | 76 | def _exec_callback(self, callback, message): 77 | try: 78 | return callback(message["ctxt"], 79 | message["publisher_id"], 80 | message["event_type"], 81 | message["payload"], 82 | message["metadata"]) 83 | except Exception: 84 | LOG.exception("Callback raised an exception.") 85 | return NotificationResult.REQUEUE 86 | 87 | def _extract_user_message(self, incoming): 88 | ctxt = self.serializer.deserialize_context(incoming.ctxt) 89 | message = incoming.message 90 | 91 | publisher_id = message.get('publisher_id') 92 | event_type = message.get('event_type') 93 | metadata = { 94 | 'message_id': message.get('message_id'), 95 | 'timestamp': message.get('timestamp') 96 | } 97 | priority = message.get('priority', '').lower() 98 | payload = self.serializer.deserialize_entity(ctxt, 99 | message.get('payload')) 100 | return priority, incoming, dict(ctxt=ctxt, 101 | publisher_id=publisher_id, 102 | event_type=event_type, 103 | payload=payload, 104 | metadata=metadata) 105 | 106 | 107 | class BatchNotificationDispatcher(NotificationDispatcher): 108 | """A message dispatcher which understands Notification messages. 109 | 110 | A MessageHandlingServer is constructed by passing a callable dispatcher 111 | which is invoked with a list of message dictionaries each time 'batch_size' 112 | messages are received or 'batch_timeout' seconds is reached. 113 | """ 114 | 115 | def dispatch(self, incoming): 116 | """Dispatch notification messages to the appropriate endpoint method. 117 | """ 118 | 119 | messages_grouped = itertools.groupby(sorted( 120 | (self._extract_user_message(m) for m in incoming), 121 | key=operator.itemgetter(0)), operator.itemgetter(0)) 122 | 123 | requeues = set() 124 | for priority, messages in messages_grouped: 125 | __, raw_messages, messages = zip(*messages) 126 | if priority not in PRIORITIES: 127 | LOG.warning('Unknown priority "%s"', priority) 128 | continue 129 | for screen, callback in self._callbacks_by_priority.get(priority, 130 | []): 131 | if screen: 132 | filtered_messages = [message for message in messages 133 | if screen.match( 134 | message["ctxt"], 135 | message["publisher_id"], 136 | message["event_type"], 137 | message["metadata"], 138 | message["payload"])] 139 | else: 140 | filtered_messages = list(messages) 141 | 142 | if not filtered_messages: 143 | continue 144 | 145 | ret = self._exec_callback(callback, filtered_messages) 146 | if ret == NotificationResult.REQUEUE: 147 | requeues.update(raw_messages) 148 | break 149 | return requeues 150 | 151 | def _exec_callback(self, callback, messages): 152 | try: 153 | return callback(messages) 154 | except Exception: 155 | LOG.exception("Callback raised an exception.") 156 | return NotificationResult.REQUEUE 157 | -------------------------------------------------------------------------------- /oslo_messaging/notify/filter.py: -------------------------------------------------------------------------------- 1 | # 2 | # Copyright 2013 eNovance 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import re 17 | 18 | 19 | class NotificationFilter: 20 | 21 | r"""Filter notification messages 22 | 23 | The NotificationFilter class is used to filter notifications that an 24 | endpoint will received. 25 | 26 | The notification can be filter on different fields: context, 27 | publisher_id, event_type, metadata and payload. 28 | 29 | The filter is done via a regular expression 30 | 31 | filter_rule = NotificationFilter( 32 | publisher_id='^compute.*', 33 | context={'tenant_id': '^5f643cfc-664b-4c69-8000-ce2ed7b08216$', 34 | 'roles': 'private'}, 35 | event_type='^compute\.instance\..*', 36 | metadata={'timestamp': 'Aug'}, 37 | payload={'state': '^active$') 38 | 39 | """ 40 | 41 | def __init__(self, context=None, publisher_id=None, event_type=None, 42 | metadata=None, payload=None): 43 | self._regex_publisher_id = None 44 | self._regex_event_type = None 45 | 46 | if publisher_id is not None: 47 | self._regex_publisher_id = re.compile(publisher_id) 48 | if event_type is not None: 49 | self._regex_event_type = re.compile(event_type) 50 | self._regexs_context = self._build_regex_dict(context) 51 | self._regexs_metadata = self._build_regex_dict(metadata) 52 | self._regexs_payload = self._build_regex_dict(payload) 53 | 54 | @staticmethod 55 | def _build_regex_dict(regex_list): 56 | if regex_list is None: 57 | return {} 58 | return {k: re.compile(regex_list[k]) for k in regex_list} 59 | 60 | @staticmethod 61 | def _check_for_single_mismatch(data, regex): 62 | if regex is None: 63 | return False 64 | if not isinstance(data, str): 65 | return True 66 | if not regex.match(data): 67 | return True 68 | return False 69 | 70 | @classmethod 71 | def _check_for_mismatch(cls, data, regex): 72 | if isinstance(regex, dict): 73 | for k in regex: 74 | if k not in data: 75 | return True 76 | if cls._check_for_single_mismatch(data[k], regex[k]): 77 | return True 78 | return False 79 | else: 80 | return cls._check_for_single_mismatch(data, regex) 81 | 82 | def match(self, context, publisher_id, event_type, metadata, payload): 83 | if (self._check_for_mismatch(publisher_id, self._regex_publisher_id) or 84 | self._check_for_mismatch(event_type, self._regex_event_type) or 85 | self._check_for_mismatch(context, self._regexs_context) or 86 | self._check_for_mismatch(metadata, self._regexs_metadata) or 87 | self._check_for_mismatch(payload, self._regexs_payload)): 88 | return False 89 | return True 90 | -------------------------------------------------------------------------------- /oslo_messaging/notify/log_handler.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import logging 14 | 15 | from oslo_config import cfg 16 | 17 | 18 | class LoggingErrorNotificationHandler(logging.Handler): 19 | def __init__(self, *args, **kwargs): 20 | # NOTE(dhellmann): Avoid a cyclical import by doing this one 21 | # at runtime. 22 | import oslo_messaging 23 | logging.Handler.__init__(self, *args, **kwargs) 24 | self._transport = oslo_messaging.get_notification_transport(cfg.CONF) 25 | self._notifier = oslo_messaging.Notifier( 26 | self._transport, 27 | publisher_id='error.publisher') 28 | 29 | def emit(self, record): 30 | conf = self._transport.conf 31 | # NOTE(bnemec): Notifier registers this opt with the transport. 32 | if ('log' in conf.oslo_messaging_notifications.driver): 33 | # NOTE(lbragstad): If we detect that log is one of the 34 | # notification drivers, then return. This protects from infinite 35 | # recursion where something bad happens, it gets logged, the log 36 | # handler sends a notification, and the log_notifier sees the 37 | # notification and logs it. 38 | return 39 | self._notifier.error({}, 40 | 'error_notification', 41 | dict(error=record.msg)) 42 | 43 | 44 | PublishErrorsHandler = LoggingErrorNotificationHandler 45 | -------------------------------------------------------------------------------- /oslo_messaging/notify/logger.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 eNovance 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | """ 15 | Driver for the Python logging package that sends log records as a notification. 16 | """ 17 | import logging 18 | 19 | from oslo_config import cfg 20 | 21 | from oslo_messaging.notify import notifier 22 | 23 | 24 | class LoggingNotificationHandler(logging.Handler): 25 | """Handler for logging to the messaging notification system. 26 | 27 | Each time the application logs a message using the :py:mod:`logging` 28 | module, it will be sent as a notification. The severity used for the 29 | notification will be the same as the one used for the log record. 30 | 31 | This can be used into a Python logging configuration this way:: 32 | 33 | [handler_notifier] 34 | class=oslo_messaging.LoggingNotificationHandler 35 | level=ERROR 36 | args=('rabbit:///') 37 | 38 | """ 39 | 40 | CONF = cfg.CONF 41 | """Default configuration object used, subclass this class if you want to 42 | use another one. 43 | 44 | """ 45 | 46 | def __init__(self, url, publisher_id=None, driver=None, 47 | topic=None, serializer=None): 48 | self.notifier = notifier.Notifier( 49 | notifier.get_notification_transport(self.CONF, url), 50 | publisher_id, driver, serializer() if serializer else None, 51 | topics=(topic if isinstance(topic, list) or topic is None 52 | else [topic])) 53 | logging.Handler.__init__(self) 54 | 55 | def emit(self, record): 56 | """Emit the log record to the messaging notification system. 57 | 58 | :param record: A log record to emit. 59 | 60 | """ 61 | method = getattr(self.notifier, record.levelname.lower(), None) 62 | 63 | if not method: 64 | return 65 | 66 | method( 67 | None, 68 | 'logrecord', 69 | { 70 | 'name': record.name, 71 | 'levelno': record.levelno, 72 | 'levelname': record.levelname, 73 | 'exc_info': record.exc_info, 74 | 'pathname': record.pathname, 75 | 'lineno': record.lineno, 76 | 'msg': record.getMessage(), 77 | 'funcName': record.funcName, 78 | 'thread': record.thread, 79 | 'processName': record.processName, 80 | 'process': record.process, 81 | 'extra': getattr(record, 'extra', None), 82 | } 83 | ) 84 | -------------------------------------------------------------------------------- /oslo_messaging/notify/messaging.py: -------------------------------------------------------------------------------- 1 | # Copyright 2011 OpenStack Foundation. 2 | # All Rights Reserved. 3 | # Copyright 2013 Red Hat, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | """ 18 | Notification drivers for sending notifications via messaging. 19 | 20 | The messaging drivers publish notification messages to notification 21 | listeners. 22 | 23 | In case of the rabbit backend the driver will block the notifier's thread 24 | until the notification message has been passed to the messaging transport. 25 | There is no guarantee that the notification message will be consumed by a 26 | notification listener. 27 | 28 | In case of the kafka backend the driver will not block the notifier's thread 29 | but return immediately. The driver will try to deliver the message in the 30 | background. 31 | 32 | Notification messages are sent 'at-most-once' - ensuring that they are not 33 | duplicated. 34 | 35 | If the connection to the messaging service is not active when a notification is 36 | sent the rabbit backend will block waiting for the connection to complete. 37 | If the connection fails to complete, the driver will try to re-establish that 38 | connection. By default this will continue indefinitely until the connection 39 | completes. However, the retry parameter can be used to have the notification 40 | send fail. In this case an error is logged and the notifier's thread is resumed 41 | without any error. 42 | 43 | If the connection to the messaging service is not active when a notification is 44 | sent the kafka backend will return immediately and the backend tries to 45 | establish the connection and deliver the messages in the background. 46 | 47 | """ 48 | 49 | import logging 50 | 51 | import oslo_messaging 52 | from oslo_messaging.notify import notifier 53 | 54 | LOG = logging.getLogger(__name__) 55 | 56 | 57 | class MessagingDriver(notifier.Driver): 58 | 59 | """Send notifications using the 1.0 message format. 60 | 61 | This driver sends notifications over the configured messaging transport, 62 | but without any message envelope (also known as message format 1.0). 63 | 64 | This driver should only be used in cases where there are existing consumers 65 | deployed which do not support the 2.0 message format. 66 | """ 67 | 68 | def __init__(self, conf, topics, transport, version=1.0): 69 | super().__init__(conf, topics, transport) 70 | self.version = version 71 | 72 | def notify(self, ctxt, message, priority, retry): 73 | priority = priority.lower() 74 | for topic in self.topics: 75 | target = oslo_messaging.Target( 76 | topic='{}.{}'.format(topic, priority)) 77 | try: 78 | self.transport._send_notification(target, ctxt, message, 79 | version=self.version, 80 | retry=retry) 81 | except Exception: 82 | LOG.exception("Could not send notification to %(topic)s. " 83 | "Payload=%(message)s", 84 | {'topic': topic, 'message': message}) 85 | 86 | 87 | class MessagingV2Driver(MessagingDriver): 88 | 89 | "Send notifications using the 2.0 message format." 90 | 91 | def __init__(self, conf, **kwargs): 92 | super().__init__(conf, version=2.0, **kwargs) 93 | -------------------------------------------------------------------------------- /oslo_messaging/notify/middleware.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013-2014 eNovance 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | """ 16 | Send notifications on request 17 | 18 | """ 19 | import logging 20 | import os.path 21 | import sys 22 | import traceback as tb 23 | 24 | from oslo_config import cfg 25 | from oslo_middleware import base 26 | import webob.dec 27 | 28 | import oslo_messaging 29 | from oslo_messaging import notify 30 | 31 | LOG = logging.getLogger(__name__) 32 | 33 | 34 | def log_and_ignore_error(fn): 35 | def wrapped(*args, **kwargs): 36 | try: 37 | return fn(*args, **kwargs) 38 | except Exception as e: 39 | LOG.exception('An exception occurred processing ' 40 | 'the API call: %s ', e) 41 | return wrapped 42 | 43 | 44 | class RequestNotifier(base.Middleware): 45 | """Send notification on request.""" 46 | 47 | @classmethod 48 | def factory(cls, global_conf, **local_conf): 49 | """Factory method for paste.deploy.""" 50 | conf = global_conf.copy() 51 | conf.update(local_conf) 52 | 53 | def _factory(app): 54 | return cls(app, **conf) 55 | return _factory 56 | 57 | def __init__(self, app, **conf): 58 | self.notifier = notify.Notifier( 59 | oslo_messaging.get_notification_transport(cfg.CONF, 60 | conf.get('url')), 61 | publisher_id=conf.get('publisher_id', 62 | os.path.basename(sys.argv[0]))) 63 | self.service_name = conf.get('service_name') 64 | self.ignore_req_list = [x.upper().strip() for x in 65 | conf.get('ignore_req_list', '').split(',')] 66 | super().__init__(app) 67 | 68 | @staticmethod 69 | def environ_to_dict(environ): 70 | """Following PEP 333, server variables are lower case, so don't 71 | include them. 72 | 73 | """ 74 | return {k: v for k, v in environ.items() 75 | if k.isupper() and k != 'HTTP_X_AUTH_TOKEN'} 76 | 77 | @log_and_ignore_error 78 | def process_request(self, request): 79 | request.environ['HTTP_X_SERVICE_NAME'] = \ 80 | self.service_name or request.host 81 | payload = { 82 | 'request': self.environ_to_dict(request.environ), 83 | } 84 | 85 | self.notifier.info({}, 86 | 'http.request', 87 | payload) 88 | 89 | @log_and_ignore_error 90 | def process_response(self, request, response, 91 | exception=None, traceback=None): 92 | payload = { 93 | 'request': self.environ_to_dict(request.environ), 94 | } 95 | 96 | if response: 97 | payload['response'] = { 98 | 'status': response.status, 99 | 'headers': response.headers, 100 | } 101 | 102 | if exception: 103 | payload['exception'] = { 104 | 'value': repr(exception), 105 | 'traceback': tb.format_tb(traceback) 106 | } 107 | 108 | self.notifier.info({}, 109 | 'http.response', 110 | payload) 111 | 112 | @webob.dec.wsgify 113 | def __call__(self, req): 114 | if req.method in self.ignore_req_list: 115 | return req.get_response(self.application) 116 | else: 117 | self.process_request(req) 118 | try: 119 | response = req.get_response(self.application) 120 | except Exception: 121 | exc_type, value, traceback = sys.exc_info() 122 | self.process_response(req, None, value, traceback) 123 | raise 124 | else: 125 | self.process_response(req, response) 126 | return response 127 | -------------------------------------------------------------------------------- /oslo_messaging/opts.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import copy 16 | import itertools 17 | 18 | from oslo_messaging._drivers import amqp 19 | from oslo_messaging._drivers import impl_rabbit 20 | from oslo_messaging._drivers.kafka_driver import kafka_options 21 | from oslo_messaging.notify import notifier 22 | from oslo_messaging.rpc import client 23 | from oslo_messaging.rpc import dispatcher 24 | from oslo_messaging import server 25 | from oslo_messaging import transport 26 | 27 | __all__ = [ 28 | 'list_opts' 29 | ] 30 | 31 | _global_opt_lists = [ 32 | server._pool_opts, 33 | client._client_opts, 34 | transport._transport_opts, 35 | dispatcher._dispatcher_opts, 36 | ] 37 | 38 | _opts = [ 39 | (None, list(itertools.chain(*_global_opt_lists))), 40 | ('oslo_messaging_notifications', notifier._notifier_opts), 41 | ('oslo_messaging_rabbit', list( 42 | itertools.chain(amqp.amqp_opts, impl_rabbit.rabbit_opts))), 43 | ('oslo_messaging_kafka', kafka_options.KAFKA_OPTS), 44 | ] 45 | 46 | 47 | def list_opts(): 48 | """Return a list of oslo.config options available in the library. 49 | 50 | The returned list includes all oslo.config options which may be registered 51 | at runtime by the library. 52 | 53 | Each element of the list is a tuple. The first element is the name of the 54 | group under which the list of elements in the second element will be 55 | registered. A group name of None corresponds to the [DEFAULT] group in 56 | config files. 57 | 58 | This function is also discoverable via the 'oslo_messaging' entry point 59 | under the 'oslo.config.opts' namespace. 60 | 61 | The purpose of this is to allow tools like the Oslo sample config file 62 | generator to discover the options exposed to users by this library. 63 | 64 | :returns: a list of (group_name, opts) tuples 65 | """ 66 | return [(g, copy.deepcopy(o)) for g, o in _opts] 67 | 68 | 69 | def set_defaults(conf, executor_thread_pool_size=None): 70 | """Set defaults for configuration variables. 71 | 72 | Overrides default options values. 73 | 74 | :param conf: Config instance specified to set default options in it. Using 75 | of instances instead of a global config object prevents conflicts between 76 | options declaration. 77 | :type conf: oslo.config.cfg.ConfigOpts instance. 78 | 79 | :keyword executor_thread_pool_size: Size of executor thread pool. 80 | :type executor_thread_pool_size: int 81 | :default executor_thread_pool_size: None 82 | 83 | """ 84 | if executor_thread_pool_size is not None: 85 | conf.set_default('executor_thread_pool_size', 86 | executor_thread_pool_size) 87 | -------------------------------------------------------------------------------- /oslo_messaging/rpc/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | __all__ = [ 16 | 'ClientSendError', 17 | 'ExpectedException', 18 | 'NoSuchMethod', 19 | 'RPCClient', 20 | 'RPCAccessPolicyBase', 21 | 'LegacyRPCAccessPolicy', 22 | 'DefaultRPCAccessPolicy', 23 | 'ExplicitRPCAccessPolicy', 24 | 'RPCDispatcher', 25 | 'RPCDispatcherError', 26 | 'RPCVersionCapError', 27 | 'RemoteError', 28 | 'UnsupportedVersion', 29 | 'expected_exceptions', 30 | 'get_rpc_transport', 31 | 'get_rpc_server', 32 | 'get_rpc_client', 33 | 'expose' 34 | ] 35 | 36 | from .client import * 37 | from .dispatcher import * 38 | from .transport import * 39 | from .server import * 40 | -------------------------------------------------------------------------------- /oslo_messaging/rpc/transport.py: -------------------------------------------------------------------------------- 1 | # Copyright 2017 OpenStack Foundation. 2 | # All Rights Reserved. 3 | # Copyright 2017 Red Hat, Inc. 4 | # 5 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 6 | # not use this file except in compliance with the License. You may obtain 7 | # a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, software 12 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 13 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 14 | # License for the specific language governing permissions and limitations 15 | # under the License. 16 | 17 | from oslo_messaging import transport as msg_transport 18 | 19 | __all__ = [ 20 | 'get_rpc_transport' 21 | ] 22 | 23 | 24 | def get_rpc_transport(conf, url=None, 25 | allowed_remote_exmods=None, 26 | transport_cls=msg_transport.RPCTransport): 27 | """A factory method for Transport objects for RPCs. 28 | 29 | This method should be used to ensure the correct messaging functionality 30 | for RPCs. RPCs and Notifications may use separate messaging systems 31 | that utilize different drivers, different access permissions, 32 | message delivery, etc. 33 | 34 | Presently, this function works exactly the same as get_transport. It's 35 | use is recommended as disambiguates the intended use for the transport 36 | and may in the future extend functionality related to the separation of 37 | messaging backends. 38 | 39 | :param conf: the user configuration 40 | :type conf: cfg.ConfigOpts 41 | :param url: a transport URL, see :py:class:`transport.TransportURL` 42 | :type url: str or TransportURL 43 | :param allowed_remote_exmods: a list of modules which a client using this 44 | transport will deserialize remote exceptions 45 | from 46 | :type allowed_remote_exmods: list 47 | :param transport_cls: the transport class to instantiate 48 | :type transport_cls: class 49 | """ 50 | return msg_transport._get_transport( 51 | conf, url, allowed_remote_exmods, 52 | transport_cls=transport_cls) 53 | -------------------------------------------------------------------------------- /oslo_messaging/serializer.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 IBM Corp. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | """Provides the definition of a message serialization handler""" 16 | 17 | import abc 18 | 19 | from oslo_serialization import jsonutils 20 | 21 | __all__ = ['Serializer', 'NoOpSerializer', 'JsonPayloadSerializer'] 22 | 23 | 24 | class Serializer(metaclass=abc.ABCMeta): 25 | """Generic (de-)serialization definition base class.""" 26 | 27 | @abc.abstractmethod 28 | def serialize_entity(self, ctxt, entity): 29 | """Serialize something to primitive form. 30 | 31 | :param ctxt: Request context, in deserialized form 32 | :param entity: Entity to be serialized 33 | :returns: Serialized form of entity 34 | """ 35 | 36 | @abc.abstractmethod 37 | def deserialize_entity(self, ctxt, entity): 38 | """Deserialize something from primitive form. 39 | 40 | :param ctxt: Request context, in deserialized form 41 | :param entity: Primitive to be deserialized 42 | :returns: Deserialized form of entity 43 | """ 44 | 45 | @abc.abstractmethod 46 | def serialize_context(self, ctxt): 47 | """Serialize a request context into a dictionary. 48 | 49 | :param ctxt: Request context 50 | :returns: Serialized form of context 51 | """ 52 | 53 | @abc.abstractmethod 54 | def deserialize_context(self, ctxt): 55 | """Deserialize a dictionary into a request context. 56 | 57 | :param ctxt: Request context dictionary 58 | :returns: Deserialized form of entity 59 | """ 60 | 61 | 62 | class NoOpSerializer(Serializer): 63 | """A serializer that does nothing.""" 64 | 65 | def serialize_entity(self, ctxt, entity): 66 | return entity 67 | 68 | def deserialize_entity(self, ctxt, entity): 69 | return entity 70 | 71 | def serialize_context(self, ctxt): 72 | return ctxt 73 | 74 | def deserialize_context(self, ctxt): 75 | return ctxt 76 | 77 | 78 | class JsonPayloadSerializer(NoOpSerializer): 79 | @staticmethod 80 | def serialize_entity(context, entity): 81 | return jsonutils.to_primitive(entity, convert_instances=True) 82 | -------------------------------------------------------------------------------- /oslo_messaging/target.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | 16 | class Target: 17 | 18 | """Identifies the destination of messages. 19 | 20 | A Target encapsulates all the information to identify where a message 21 | should be sent or what messages a server is listening for. 22 | 23 | Different subsets of the information encapsulated in a Target object is 24 | relevant to various aspects of the API: 25 | 26 | an RPC Server's target: 27 | topic and server is required; exchange is optional 28 | an RPC endpoint's target: 29 | namespace and version is optional 30 | an RPC client sending a message: 31 | topic is required, all other attributes optional 32 | a Notification Server's target: 33 | topic is required, exchange is optional; all other attributes ignored 34 | a Notifier's target: 35 | topic is required, exchange is optional; all other attributes ignored 36 | 37 | Its attributes are: 38 | 39 | :param exchange: A scope for topics. Leave unspecified to default to the 40 | control_exchange configuration option. 41 | :type exchange: str 42 | :param topic: A name which identifies the set of interfaces exposed by a 43 | server. Multiple servers may listen on a topic and messages will be 44 | dispatched to one of the servers selected in a best-effort round-robin 45 | fashion (unless fanout is ``True``). 46 | :type topic: str 47 | :param namespace: Identifies a particular RPC interface (i.e. set of 48 | methods) exposed by a server. The default interface has no namespace 49 | identifier and is referred to as the null namespace. 50 | :type namespace: str 51 | :param version: RPC interfaces have a major.minor version number associated 52 | with them. A minor number increment indicates a backwards compatible 53 | change and an incompatible change is indicated by a major number bump. 54 | Servers may implement multiple major versions and clients may require 55 | indicate that their message requires a particular minimum minor version. 56 | :type version: str 57 | :param server: RPC Clients can request that a message be directed to a 58 | specific server, rather than just one of a pool of servers listening on 59 | the topic. 60 | :type server: str 61 | :param fanout: Clients may request that a copy of the message be delivered 62 | to all servers listening on a topic by setting fanout to ``True``, rather 63 | than just one of them. 64 | :type fanout: bool 65 | :param legacy_namespaces: A server always accepts messages specified via 66 | the 'namespace' parameter, and may also accept messages defined via 67 | this parameter. This option should be used to switch namespaces safely 68 | during rolling upgrades. 69 | :type legacy_namespaces: list of strings 70 | """ 71 | 72 | def __init__(self, exchange=None, topic=None, namespace=None, 73 | version=None, server=None, fanout=None, 74 | legacy_namespaces=None): 75 | self.exchange = exchange 76 | self.topic = topic 77 | self.namespace = namespace 78 | self.version = version 79 | self.server = server 80 | self.fanout = fanout 81 | self.accepted_namespaces = [namespace] + (legacy_namespaces or []) 82 | 83 | def __call__(self, **kwargs): 84 | for a in ('exchange', 'topic', 'namespace', 85 | 'version', 'server', 'fanout'): 86 | kwargs.setdefault(a, getattr(self, a)) 87 | return Target(**kwargs) 88 | 89 | def __eq__(self, other): 90 | return vars(self) == vars(other) 91 | 92 | def __ne__(self, other): 93 | return not self == other 94 | 95 | def __repr__(self): 96 | attrs = [] 97 | for a in ['exchange', 'topic', 'namespace', 98 | 'version', 'server', 'fanout']: 99 | v = getattr(self, a) 100 | if v: 101 | attrs.append((a, v)) 102 | values = ', '.join(['%s=%s' % i for i in attrs]) 103 | return '' 104 | 105 | def __hash__(self): 106 | return id(self) 107 | -------------------------------------------------------------------------------- /oslo_messaging/tests/__init__.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 eNovance 2 | # All Rights Reserved. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import eventlet 17 | eventlet.monkey_patch() 18 | -------------------------------------------------------------------------------- /oslo_messaging/tests/drivers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/oslo_messaging/tests/drivers/__init__.py -------------------------------------------------------------------------------- /oslo_messaging/tests/drivers/test_pool.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import threading 16 | import uuid 17 | 18 | import fixtures 19 | import testscenarios 20 | 21 | from oslo_messaging._drivers import pool 22 | from oslo_messaging.tests import utils as test_utils 23 | 24 | load_tests = testscenarios.load_tests_apply_scenarios 25 | 26 | 27 | class PoolTestCase(test_utils.BaseTestCase): 28 | 29 | _max_size = [ 30 | ('default_size', dict(max_size=None, n_iters=4)), 31 | ('set_max_size', dict(max_size=10, n_iters=10)), 32 | ] 33 | 34 | _create_error = [ 35 | ('no_create_error', dict(create_error=False)), 36 | ('create_error', dict(create_error=True)), 37 | ] 38 | 39 | @classmethod 40 | def generate_scenarios(cls): 41 | cls.scenarios = testscenarios.multiply_scenarios(cls._max_size, 42 | cls._create_error) 43 | 44 | class TestPool(pool.Pool): 45 | 46 | def create(self, retry=None): 47 | return uuid.uuid4() 48 | 49 | class ThreadWaitWaiter: 50 | 51 | """A gross hack. 52 | 53 | Stub out the condition variable's wait() method and spin until it 54 | has been called by each thread. 55 | """ 56 | 57 | def __init__(self, cond, n_threads, test): 58 | self.cond = cond 59 | self.test = test 60 | self.n_threads = n_threads 61 | self.n_waits = 0 62 | self.orig_wait = cond.wait 63 | 64 | def count_waits(**kwargs): 65 | self.n_waits += 1 66 | self.orig_wait(**kwargs) 67 | self.test.useFixture(fixtures.MockPatchObject( 68 | self.cond, 'wait', count_waits)) 69 | 70 | def wait(self): 71 | while self.n_waits < self.n_threads: 72 | pass 73 | self.test.useFixture(fixtures.MockPatchObject( 74 | self.cond, 'wait', self.orig_wait)) 75 | 76 | def test_pool(self): 77 | kwargs = {} 78 | if self.max_size is not None: 79 | kwargs['max_size'] = self.max_size 80 | 81 | p = self.TestPool(**kwargs) 82 | 83 | if self.create_error: 84 | def create_error(retry=None): 85 | raise RuntimeError 86 | orig_create = p.create 87 | self.useFixture(fixtures.MockPatchObject( 88 | p, 'create', create_error)) 89 | self.assertRaises(RuntimeError, p.get) 90 | self.useFixture(fixtures.MockPatchObject( 91 | p, 'create', orig_create)) 92 | 93 | objs = [] 94 | for i in range(self.n_iters): 95 | objs.append(p.get()) 96 | self.assertIsInstance(objs[i], uuid.UUID) 97 | 98 | def wait_for_obj(): 99 | o = p.get() 100 | self.assertIn(o, objs) 101 | 102 | waiter = self.ThreadWaitWaiter(p._cond, self.n_iters, self) 103 | 104 | threads = [] 105 | for i in range(self.n_iters): 106 | t = threading.Thread(target=wait_for_obj) 107 | t.start() 108 | threads.append(t) 109 | 110 | waiter.wait() 111 | 112 | for o in objs: 113 | p.put(o) 114 | 115 | for t in threads: 116 | t.join() 117 | 118 | for o in objs: 119 | p.put(o) 120 | 121 | for o in p.iter_free(): 122 | self.assertIn(o, objs) 123 | objs.remove(o) 124 | 125 | self.assertEqual([], objs) 126 | 127 | 128 | PoolTestCase.generate_scenarios() 129 | -------------------------------------------------------------------------------- /oslo_messaging/tests/functional/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/oslo_messaging/tests/functional/__init__.py -------------------------------------------------------------------------------- /oslo_messaging/tests/functional/notify/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/oslo_messaging/tests/functional/notify/__init__.py -------------------------------------------------------------------------------- /oslo_messaging/tests/functional/notify/test_logger.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 NetEase Corp. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import logging 16 | import uuid 17 | 18 | import testscenarios 19 | 20 | import oslo_messaging 21 | from oslo_messaging.tests.functional import utils 22 | 23 | load_tests = testscenarios.load_tests_apply_scenarios 24 | 25 | 26 | class LoggingNotificationHandlerTestCase(utils.SkipIfNoTransportURL): 27 | """Test case for `oslo_messaging.LoggingNotificationHandler` 28 | 29 | Build up a logger using this handler, then test logging under messaging and 30 | messagingv2 driver. Make sure receive expected logging notifications. 31 | """ 32 | 33 | _priority = [ 34 | ('debug', dict(priority='debug')), 35 | ('info', dict(priority='info')), 36 | ('warn', dict(priority='warn')), 37 | ('error', dict(priority='error')), 38 | ('critical', dict(priority='critical')), 39 | ] 40 | 41 | _driver = [ 42 | ('messaging', dict(driver='messaging')), 43 | ('messagingv2', dict(driver='messagingv2')), 44 | ] 45 | 46 | @classmethod 47 | def generate_scenarios(cls): 48 | cls.scenarios = testscenarios.multiply_scenarios(cls._priority, 49 | cls._driver) 50 | 51 | def test_logging(self): 52 | # NOTE(gtt): Using different topic to make tests run in parallel 53 | topic = 'test_logging_{}_driver_{}'.format(self.priority, self.driver) 54 | 55 | if self.notify_url.startswith("kafka://"): 56 | self.conf.set_override('consumer_group', str(uuid.uuid4()), 57 | group='oslo_messaging_kafka') 58 | 59 | self.config(driver=[self.driver], 60 | topics=[topic], 61 | group='oslo_messaging_notifications') 62 | 63 | listener = self.useFixture( 64 | utils.NotificationFixture(self.conf, self.notify_url, [topic])) 65 | 66 | log_notify = oslo_messaging.LoggingNotificationHandler(self.notify_url) 67 | 68 | log = logging.getLogger(topic) 69 | log.setLevel(logging.DEBUG) 70 | log.addHandler(log_notify) 71 | 72 | log_method = getattr(log, self.priority) 73 | log_method('Test logging at priority: %s' % self.priority) 74 | 75 | events = listener.get_events(timeout=15) 76 | self.assertEqual(1, len(events)) 77 | 78 | info_event = events[0] 79 | 80 | self.assertEqual(self.priority, info_event[0]) 81 | self.assertEqual('logrecord', info_event[1]) 82 | 83 | for key in ['name', 'thread', 'extra', 'process', 'funcName', 84 | 'levelno', 'processName', 'pathname', 'lineno', 85 | 'msg', 'exc_info', 'levelname']: 86 | self.assertIn(key, info_event[2]) 87 | 88 | 89 | LoggingNotificationHandlerTestCase.generate_scenarios() 90 | -------------------------------------------------------------------------------- /oslo_messaging/tests/functional/test_rabbitmq.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | 14 | import os 15 | import signal 16 | import time 17 | 18 | import fixtures 19 | from pifpaf.drivers import rabbitmq 20 | 21 | from oslo_messaging.tests.functional import utils 22 | from oslo_messaging.tests import utils as test_utils 23 | 24 | 25 | class ConnectedPortMatcher: 26 | def __init__(self, port): 27 | self.port = port 28 | 29 | def __eq__(self, data): 30 | return data.get("port") == self.port 31 | 32 | def __repr__(self): 33 | return "" % self.port 34 | 35 | 36 | class RabbitMQFailoverTests(test_utils.BaseTestCase): 37 | DRIVERS = [ 38 | "rabbit", 39 | ] 40 | 41 | def test_failover_scenario(self): 42 | self._test_failover_scenario() 43 | 44 | def test_failover_scenario_enable_cancel_on_failover(self): 45 | self._test_failover_scenario(enable_cancel_on_failover=True) 46 | 47 | def _test_failover_scenario(self, enable_cancel_on_failover=False): 48 | # NOTE(sileht): run this test only if functional suite run of a driver 49 | # that use rabbitmq as backend 50 | self.driver = os.environ.get('TRANSPORT_DRIVER') 51 | if self.driver not in self.DRIVERS: 52 | self.skipTest("TRANSPORT_DRIVER is not set to a rabbit driver") 53 | 54 | # NOTE(sileht): Allow only one response at a time, to 55 | # have only one tcp connection for reply and ensure it will failover 56 | # correctly 57 | self.config(heartbeat_timeout_threshold=1, 58 | rpc_conn_pool_size=1, 59 | kombu_reconnect_delay=0, 60 | rabbit_retry_interval=0, 61 | rabbit_retry_backoff=0, 62 | enable_cancel_on_failover=enable_cancel_on_failover, 63 | group='oslo_messaging_rabbit') 64 | 65 | self.pifpaf = self.useFixture(rabbitmq.RabbitMQDriver(cluster=True, 66 | port=5692)) 67 | 68 | self.url = self.pifpaf.env["PIFPAF_URL"] 69 | self.n1 = self.pifpaf.env["PIFPAF_RABBITMQ_NODENAME1"] 70 | self.n2 = self.pifpaf.env["PIFPAF_RABBITMQ_NODENAME2"] 71 | self.n3 = self.pifpaf.env["PIFPAF_RABBITMQ_NODENAME3"] 72 | 73 | # ensure connections will be establish to the first node 74 | self.pifpaf.stop_node(self.n2) 75 | self.pifpaf.stop_node(self.n3) 76 | 77 | self.servers = self.useFixture(utils.RpcServerGroupFixture( 78 | self.conf, self.url, endpoint=self, names=["server"])) 79 | 80 | # Don't randomize rabbit hosts 81 | self.useFixture(fixtures.MockPatch( 82 | 'oslo_messaging._drivers.impl_rabbit.random', 83 | side_effect=lambda x: x)) 84 | 85 | # NOTE(sileht): this connects server connections and reply 86 | # connection to nodename n1 87 | self.client = self.servers.client(0) 88 | self.client.ping() 89 | self._check_ports(self.pifpaf.port) 90 | 91 | # Switch to node n2 92 | self.pifpaf.start_node(self.n2) 93 | self.assertEqual("callback done", self.client.kill_and_process()) 94 | self.assertEqual("callback done", self.client.just_process()) 95 | self._check_ports(self.pifpaf.get_port(self.n2)) 96 | 97 | # Switch to node n3 98 | self.pifpaf.start_node(self.n3) 99 | time.sleep(0.1) 100 | self.pifpaf.kill_node(self.n2, signal=signal.SIGKILL) 101 | time.sleep(0.1) 102 | self.assertEqual("callback done", self.client.just_process()) 103 | self._check_ports(self.pifpaf.get_port(self.n3)) 104 | 105 | self.pifpaf.start_node(self.n1) 106 | time.sleep(0.1) 107 | self.pifpaf.kill_node(self.n3, signal=signal.SIGKILL) 108 | time.sleep(0.1) 109 | self.assertEqual("callback done", self.client.just_process()) 110 | self._check_ports(self.pifpaf.get_port(self.n1)) 111 | 112 | def kill_and_process(self, *args, **kargs): 113 | self.pifpaf.kill_node(self.n1, signal=signal.SIGKILL) 114 | time.sleep(0.1) 115 | return "callback done" 116 | 117 | def just_process(self, *args, **kargs): 118 | return "callback done" 119 | 120 | def _check_ports(self, port): 121 | rpc_server = self.servers.servers[0].server 122 | connection_contexts = [ 123 | # rpc server 124 | rpc_server.listener._poll_style_listener.conn, 125 | # rpc client 126 | self.client.client.transport._driver._get_connection(), 127 | # rpc client replies waiter 128 | self.client.client.transport._driver._reply_q_conn, 129 | ] 130 | 131 | ports = [cctxt.connection.channel.connection.sock.getpeername()[1] 132 | for cctxt in connection_contexts] 133 | 134 | self.assertEqual([port] * len(ports), ports, 135 | "expected: %s, rpc-server: %s, rpc-client: %s, " 136 | "rpc-replies: %s" % tuple([port] + ports)) 137 | -------------------------------------------------------------------------------- /oslo_messaging/tests/notify/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/oslo_messaging/tests/notify/__init__.py -------------------------------------------------------------------------------- /oslo_messaging/tests/notify/test_log_handler.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 2 | # not use this file except in compliance with the License. You may obtain 3 | # a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 9 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | # License for the specific language governing permissions and limitations 11 | # under the License. 12 | 13 | import logging 14 | 15 | import fixtures 16 | 17 | import oslo_messaging 18 | from oslo_messaging.notify import log_handler 19 | from oslo_messaging.tests import utils as test_utils 20 | from unittest import mock 21 | 22 | 23 | class PublishErrorsHandlerTestCase(test_utils.BaseTestCase): 24 | """Tests for log.PublishErrorsHandler""" 25 | def setUp(self): 26 | super().setUp() 27 | self.publisherrorshandler = (log_handler. 28 | PublishErrorsHandler(logging.ERROR)) 29 | 30 | def test_emit_cfg_log_notifier_in_notifier_drivers(self): 31 | drivers = ['messaging', 'log'] 32 | self.config(driver=drivers, 33 | group='oslo_messaging_notifications') 34 | self.stub_flg = True 35 | 36 | transport = oslo_messaging.get_notification_transport(self.conf) 37 | notifier = oslo_messaging.Notifier(transport) 38 | 39 | def fake_notifier(*args, **kwargs): 40 | self.stub_flg = False 41 | 42 | self.useFixture(fixtures.MockPatchObject( 43 | notifier, 'error', fake_notifier)) 44 | 45 | logrecord = logging.LogRecord(name='name', level='WARN', 46 | pathname='/tmp', lineno=1, msg='Message', 47 | args=None, exc_info=None) 48 | self.publisherrorshandler.emit(logrecord) 49 | self.assertTrue(self.stub_flg) 50 | 51 | @mock.patch('oslo_messaging.notify.notifier.Notifier._notify') 52 | def test_emit_notification(self, mock_notify): 53 | logrecord = logging.LogRecord(name='name', level='ERROR', 54 | pathname='/tmp', lineno=1, msg='Message', 55 | args=None, exc_info=None) 56 | self.publisherrorshandler.emit(logrecord) 57 | self.assertEqual('error.publisher', 58 | self.publisherrorshandler._notifier.publisher_id) 59 | mock_notify.assert_called_with({}, 60 | 'error_notification', 61 | {'error': 'Message'}, 'ERROR') 62 | -------------------------------------------------------------------------------- /oslo_messaging/tests/notify/test_logger.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 eNovance 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import datetime 16 | import logging 17 | import logging.config 18 | import os 19 | import sys 20 | 21 | from oslo_utils import timeutils 22 | import testscenarios 23 | 24 | import oslo_messaging 25 | from oslo_messaging.tests import utils as test_utils 26 | from unittest import mock 27 | 28 | 29 | load_tests = testscenarios.load_tests_apply_scenarios 30 | 31 | # Stolen from oslo.log 32 | logging.AUDIT = logging.INFO + 1 33 | logging.addLevelName(logging.AUDIT, 'AUDIT') 34 | 35 | 36 | class TestLogNotifier(test_utils.BaseTestCase): 37 | 38 | scenarios = [ 39 | ('debug', dict(priority='debug')), 40 | ('info', dict(priority='info')), 41 | ('warning', dict(priority='warning', queue='WARN')), 42 | ('warn', dict(priority='warn')), 43 | ('error', dict(priority='error')), 44 | ('critical', dict(priority='critical')), 45 | ('audit', dict(priority='audit')), 46 | ] 47 | 48 | def setUp(self): 49 | super().setUp() 50 | self.addCleanup(oslo_messaging.notify._impl_test.reset) 51 | self.config(driver=['test'], 52 | group='oslo_messaging_notifications') 53 | # NOTE(jamespage) disable thread information logging for testing 54 | # as this can cause test failures when monkey_patch via eventlet 55 | logging.logThreads = 0 56 | 57 | @mock.patch('oslo_utils.timeutils.utcnow') 58 | def test_logger(self, mock_utcnow): 59 | fake_transport = oslo_messaging.get_notification_transport(self.conf) 60 | with mock.patch('oslo_messaging.transport._get_transport', 61 | return_value=fake_transport): 62 | self.logger = oslo_messaging.LoggingNotificationHandler('test://') 63 | 64 | mock_utcnow.return_value = datetime.datetime.utcnow() 65 | 66 | levelno = getattr(logging, self.priority.upper(), 42) 67 | 68 | record = logging.LogRecord('foo', 69 | levelno, 70 | '/foo/bar', 71 | 42, 72 | 'Something happened', 73 | None, 74 | None) 75 | 76 | self.logger.emit(record) 77 | 78 | context = oslo_messaging.notify._impl_test.NOTIFICATIONS[0][0] 79 | self.assertEqual({}, context) 80 | 81 | n = oslo_messaging.notify._impl_test.NOTIFICATIONS[0][1] 82 | self.assertEqual(getattr(self, 'queue', self.priority.upper()), 83 | n['priority']) 84 | self.assertEqual('logrecord', n['event_type']) 85 | self.assertEqual(str(timeutils.utcnow()), n['timestamp']) 86 | self.assertIsNone(n['publisher_id']) 87 | self.assertEqual( 88 | {'process': os.getpid(), 89 | 'funcName': None, 90 | 'name': 'foo', 91 | 'thread': None, 92 | 'levelno': levelno, 93 | 'processName': 'MainProcess', 94 | 'pathname': '/foo/bar', 95 | 'lineno': 42, 96 | 'msg': 'Something happened', 97 | 'exc_info': None, 98 | 'levelname': logging.getLevelName(levelno), 99 | 'extra': None}, 100 | n['payload']) 101 | 102 | @mock.patch('oslo_utils.timeutils.utcnow') 103 | def test_logging_conf(self, mock_utcnow): 104 | fake_transport = oslo_messaging.get_notification_transport(self.conf) 105 | with mock.patch('oslo_messaging.transport._get_transport', 106 | return_value=fake_transport): 107 | logging.config.dictConfig({ 108 | 'version': 1, 109 | 'handlers': { 110 | 'notification': { 111 | 'class': 'oslo_messaging.LoggingNotificationHandler', 112 | 'level': self.priority.upper(), 113 | 'url': 'test://', 114 | }, 115 | }, 116 | 'loggers': { 117 | 'default': { 118 | 'handlers': ['notification'], 119 | 'level': self.priority.upper(), 120 | }, 121 | }, 122 | }) 123 | 124 | mock_utcnow.return_value = datetime.datetime.utcnow() 125 | 126 | levelno = getattr(logging, self.priority.upper()) 127 | 128 | logger = logging.getLogger('default') 129 | lineno = sys._getframe().f_lineno + 1 130 | logger.log(levelno, 'foobar') 131 | 132 | n = oslo_messaging.notify._impl_test.NOTIFICATIONS[0][1] 133 | self.assertEqual(getattr(self, 'queue', self.priority.upper()), 134 | n['priority']) 135 | self.assertEqual('logrecord', n['event_type']) 136 | self.assertEqual(str(timeutils.utcnow()), n['timestamp']) 137 | self.assertIsNone(n['publisher_id']) 138 | pathname = __file__ 139 | if pathname.endswith(('.pyc', '.pyo')): 140 | pathname = pathname[:-1] 141 | self.assertDictEqual( 142 | n['payload'], 143 | {'process': os.getpid(), 144 | 'funcName': 'test_logging_conf', 145 | 'name': 'default', 146 | 'thread': None, 147 | 'levelno': levelno, 148 | 'processName': 'MainProcess', 149 | 'pathname': pathname, 150 | 'lineno': lineno, 151 | 'msg': 'foobar', 152 | 'exc_info': None, 153 | 'levelname': logging.getLevelName(levelno), 154 | 'extra': None}) 155 | -------------------------------------------------------------------------------- /oslo_messaging/tests/rpc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/oslo_messaging/tests/rpc/__init__.py -------------------------------------------------------------------------------- /oslo_messaging/tests/test_config_opts_proxy.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 Mirantis, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from oslo_config import cfg 16 | from oslo_config import types 17 | 18 | from oslo_messaging._drivers import common as drv_cmn 19 | from oslo_messaging.tests import utils as test_utils 20 | from oslo_messaging import transport 21 | 22 | 23 | class TestConfigOptsProxy(test_utils.BaseTestCase): 24 | 25 | def test_rabbit(self): 26 | group = 'oslo_messaging_rabbit' 27 | self.config(rabbit_retry_interval=1, 28 | rabbit_qos_prefetch_count=0, 29 | group=group) 30 | dummy_opts = [cfg.ListOpt('list_str', item_type=types.String(), 31 | default=[]), 32 | cfg.ListOpt('list_int', item_type=types.Integer(), 33 | default=[]), 34 | cfg.DictOpt('dict', default={}), 35 | cfg.BoolOpt('bool', default=False), 36 | cfg.StrOpt('str', default='default')] 37 | self.conf.register_opts(dummy_opts, group=group) 38 | url = transport.TransportURL.parse( 39 | self.conf, "rabbit:///" 40 | "?rabbit_qos_prefetch_count=2" 41 | "&list_str=1&list_str=2&list_str=3" 42 | "&list_int=1&list_int=2&list_int=3" 43 | "&dict=x:1&dict=y:2&dict=z:3" 44 | "&bool=True" 45 | ) 46 | conf = drv_cmn.ConfigOptsProxy(self.conf, url, group) 47 | self.assertRaises(cfg.NoSuchOptError, 48 | conf.__getattr__, 49 | 'unknown_group') 50 | self.assertIsInstance(getattr(conf, group), 51 | conf.GroupAttrProxy) 52 | self.assertEqual(1, conf.oslo_messaging_rabbit.rabbit_retry_interval) 53 | self.assertEqual(2, 54 | conf.oslo_messaging_rabbit.rabbit_qos_prefetch_count) 55 | self.assertEqual(['1', '2', '3'], conf.oslo_messaging_rabbit.list_str) 56 | self.assertEqual([1, 2, 3], conf.oslo_messaging_rabbit.list_int) 57 | self.assertEqual({'x': '1', 'y': '2', 'z': '3'}, 58 | conf.oslo_messaging_rabbit.dict) 59 | self.assertEqual(True, conf.oslo_messaging_rabbit.bool) 60 | self.assertEqual('default', conf.oslo_messaging_rabbit.str) 61 | 62 | def test_not_in_group(self): 63 | group = 'oslo_messaging_rabbit' 64 | url = transport.TransportURL.parse( 65 | self.conf, "rabbit:///?unknown_opt=4" 66 | ) 67 | self.assertRaises(cfg.NoSuchOptError, 68 | drv_cmn.ConfigOptsProxy, 69 | self.conf, url, group) 70 | 71 | def test_invalid_value(self): 72 | group = 'oslo_messaging_rabbit' 73 | self.config(kombu_reconnect_delay=1.0, 74 | group=group) 75 | url = transport.TransportURL.parse( 76 | self.conf, "rabbit:///?kombu_reconnect_delay=invalid_value" 77 | ) 78 | self.assertRaises(ValueError, drv_cmn.ConfigOptsProxy, self.conf, 79 | url, group) 80 | -------------------------------------------------------------------------------- /oslo_messaging/tests/test_expected_exceptions.py: -------------------------------------------------------------------------------- 1 | # Copyright 2012 OpenStack Foundation 2 | # Copyright 2013 Red Hat, Inc. 3 | # 4 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 5 | # not use this file except in compliance with the License. You may obtain 6 | # a copy of the License at 7 | # 8 | # http://www.apache.org/licenses/LICENSE-2.0 9 | # 10 | # Unless required by applicable law or agreed to in writing, software 11 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 12 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 13 | # License for the specific language governing permissions and limitations 14 | # under the License. 15 | 16 | import oslo_messaging 17 | from oslo_messaging.tests import utils as test_utils 18 | 19 | 20 | class TestExpectedExceptions(test_utils.BaseTestCase): 21 | 22 | def test_exception(self): 23 | e = None 24 | try: 25 | try: 26 | raise ValueError() 27 | except Exception: 28 | raise oslo_messaging.ExpectedException() 29 | except oslo_messaging.ExpectedException as e: # noqa: F841 30 | self.assertIsInstance(e, oslo_messaging.ExpectedException) 31 | self.assertTrue(hasattr(e, 'exc_info')) 32 | self.assertIsInstance(e.exc_info[1], ValueError) 33 | 34 | def test_decorator_expected(self): 35 | class FooException(Exception): 36 | pass 37 | 38 | @oslo_messaging.expected_exceptions(FooException) 39 | def naughty(): 40 | raise FooException() 41 | 42 | self.assertRaises(oslo_messaging.ExpectedException, naughty) 43 | 44 | def test_decorator_expected_subclass(self): 45 | class FooException(Exception): 46 | pass 47 | 48 | class BarException(FooException): 49 | pass 50 | 51 | @oslo_messaging.expected_exceptions(FooException) 52 | def naughty(): 53 | raise BarException() 54 | 55 | self.assertRaises(oslo_messaging.ExpectedException, naughty) 56 | 57 | def test_decorator_unexpected(self): 58 | class FooException(Exception): 59 | pass 60 | 61 | @oslo_messaging.expected_exceptions(FooException) 62 | def really_naughty(): 63 | raise ValueError() 64 | 65 | self.assertRaises(ValueError, really_naughty) 66 | -------------------------------------------------------------------------------- /oslo_messaging/tests/test_fixture.py: -------------------------------------------------------------------------------- 1 | # Copyright 2015 Mirantis Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from oslo_config import cfg 16 | 17 | from oslo_messaging import conffixture 18 | from oslo_messaging.tests import utils as test_utils 19 | 20 | 21 | class TestConfFixture(test_utils.BaseTestCase): 22 | 23 | def test_fixture_wraps_set_override(self): 24 | conf = self.messaging_conf.conf 25 | self.assertIsNotNone(conf.set_override.wrapped) 26 | self.messaging_conf._teardown_decorator() 27 | self.assertFalse(hasattr(conf.set_override, 'wrapped')) 28 | 29 | def test_fixture_wraps_clear_override(self): 30 | conf = self.messaging_conf.conf 31 | self.assertIsNotNone(conf.clear_override.wrapped) 32 | self.messaging_conf._teardown_decorator() 33 | self.assertFalse(hasattr(conf.clear_override, 'wrapped')) 34 | 35 | def test_fixture_setup_teardown_decorator(self): 36 | conf = cfg.ConfigOpts() 37 | self.assertFalse(hasattr(conf.set_override, 'wrapped')) 38 | self.assertFalse(hasattr(conf.clear_override, 'wrapped')) 39 | fixture = conffixture.ConfFixture(conf) 40 | self.assertFalse(hasattr(conf.set_override, 'wrapped')) 41 | self.assertFalse(hasattr(conf.clear_override, 'wrapped')) 42 | self.useFixture(fixture) 43 | self.assertTrue(hasattr(conf.set_override, 'wrapped')) 44 | self.assertTrue(hasattr(conf.clear_override, 'wrapped')) 45 | fixture._teardown_decorator() 46 | self.assertFalse(hasattr(conf.set_override, 'wrapped')) 47 | self.assertFalse(hasattr(conf.clear_override, 'wrapped')) 48 | 49 | def test_fixture_properties(self): 50 | conf = self.messaging_conf.conf 51 | self.messaging_conf.transport_url = 'fake:/vhost' 52 | self.assertEqual('fake:/vhost', self.messaging_conf.transport_url) 53 | self.assertEqual('fake:/vhost', conf.transport_url) 54 | 55 | def test_old_notifications_config_override(self): 56 | conf = self.messaging_conf.conf 57 | conf.set_override( 58 | "notification_driver", ["messaging"]) 59 | conf.set_override( 60 | "notification_transport_url", "http://xyz") 61 | conf.set_override( 62 | "notification_topics", ['topic1']) 63 | 64 | self.assertEqual(["messaging"], 65 | conf.oslo_messaging_notifications.driver) 66 | self.assertEqual("http://xyz", 67 | conf.oslo_messaging_notifications.transport_url) 68 | self.assertEqual(['topic1'], 69 | conf.oslo_messaging_notifications.topics) 70 | 71 | conf.clear_override("notification_driver") 72 | conf.clear_override("notification_transport_url") 73 | conf.clear_override("notification_topics") 74 | 75 | self.assertEqual([], 76 | conf.oslo_messaging_notifications.driver) 77 | self.assertIsNone(conf.oslo_messaging_notifications.transport_url) 78 | self.assertEqual(['notifications'], 79 | conf.oslo_messaging_notifications.topics) 80 | -------------------------------------------------------------------------------- /oslo_messaging/tests/test_opts.py: -------------------------------------------------------------------------------- 1 | # Copyright 2014 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | from unittest import mock 16 | 17 | import stevedore 18 | import testtools 19 | 20 | from oslo_messaging import server 21 | try: 22 | from oslo_messaging import opts 23 | except ImportError: 24 | opts = None 25 | from oslo_messaging.tests import utils as test_utils 26 | 27 | 28 | @testtools.skipIf(opts is None, "Options not importable") 29 | class OptsTestCase(test_utils.BaseTestCase): 30 | 31 | def _test_list_opts(self, result): 32 | self.assertEqual(4, len(result)) 33 | 34 | groups = [g for (g, l) in result] 35 | self.assertIn(None, groups) 36 | self.assertIn('oslo_messaging_notifications', groups) 37 | self.assertIn('oslo_messaging_rabbit', groups) 38 | self.assertIn('oslo_messaging_kafka', groups) 39 | 40 | def test_list_opts(self): 41 | self._test_list_opts(opts.list_opts()) 42 | 43 | def test_entry_point(self): 44 | result = None 45 | for ext in stevedore.ExtensionManager('oslo.config.opts', 46 | invoke_on_load=True): 47 | if ext.name == "oslo.messaging": 48 | result = ext.obj 49 | break 50 | 51 | self.assertIsNotNone(result) 52 | self._test_list_opts(result) 53 | 54 | def test_defaults(self): 55 | transport = mock.Mock() 56 | transport.conf = self.conf 57 | 58 | class MessageHandlingServerImpl(server.MessageHandlingServer): 59 | def _create_listener(self): 60 | pass 61 | 62 | def _process_incoming(self, incoming): 63 | pass 64 | 65 | MessageHandlingServerImpl(transport, mock.Mock()) 66 | opts.set_defaults(self.conf, executor_thread_pool_size=100) 67 | self.assertEqual(100, self.conf.executor_thread_pool_size) 68 | -------------------------------------------------------------------------------- /oslo_messaging/tests/test_target.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | import testscenarios 16 | 17 | import oslo_messaging 18 | from oslo_messaging.tests import utils as test_utils 19 | 20 | load_tests = testscenarios.load_tests_apply_scenarios 21 | 22 | 23 | class TargetConstructorTestCase(test_utils.BaseTestCase): 24 | 25 | scenarios = [ 26 | ('all_none', dict(kwargs=dict())), 27 | ('exchange', dict(kwargs=dict(exchange='testexchange'))), 28 | ('topic', dict(kwargs=dict(topic='testtopic'))), 29 | ('namespace', dict(kwargs=dict(namespace='testnamespace'))), 30 | ('version', dict(kwargs=dict(version='3.4'))), 31 | ('server', dict(kwargs=dict(server='testserver'))), 32 | ('fanout', dict(kwargs=dict(fanout=True))), 33 | ] 34 | 35 | def test_constructor(self): 36 | target = oslo_messaging.Target(**self.kwargs) 37 | for k in self.kwargs: 38 | self.assertEqual(self.kwargs[k], getattr(target, k)) 39 | for k in ['exchange', 'topic', 'namespace', 40 | 'version', 'server', 'fanout']: 41 | if k in self.kwargs: 42 | continue 43 | self.assertIsNone(getattr(target, k)) 44 | 45 | 46 | class TargetCallableTestCase(test_utils.BaseTestCase): 47 | 48 | scenarios = [ 49 | ('all_none', dict(attrs=dict(), kwargs=dict(), vals=dict())), 50 | ('exchange_attr', dict(attrs=dict(exchange='testexchange'), 51 | kwargs=dict(), 52 | vals=dict(exchange='testexchange'))), 53 | ('exchange_arg', dict(attrs=dict(), 54 | kwargs=dict(exchange='testexchange'), 55 | vals=dict(exchange='testexchange'))), 56 | ('topic_attr', dict(attrs=dict(topic='testtopic'), 57 | kwargs=dict(), 58 | vals=dict(topic='testtopic'))), 59 | ('topic_arg', dict(attrs=dict(), 60 | kwargs=dict(topic='testtopic'), 61 | vals=dict(topic='testtopic'))), 62 | ('namespace_attr', dict(attrs=dict(namespace='testnamespace'), 63 | kwargs=dict(), 64 | vals=dict(namespace='testnamespace'))), 65 | ('namespace_arg', dict(attrs=dict(), 66 | kwargs=dict(namespace='testnamespace'), 67 | vals=dict(namespace='testnamespace'))), 68 | ('version_attr', dict(attrs=dict(version='3.4'), 69 | kwargs=dict(), 70 | vals=dict(version='3.4'))), 71 | ('version_arg', dict(attrs=dict(), 72 | kwargs=dict(version='3.4'), 73 | vals=dict(version='3.4'))), 74 | ('server_attr', dict(attrs=dict(server='testserver'), 75 | kwargs=dict(), 76 | vals=dict(server='testserver'))), 77 | ('server_arg', dict(attrs=dict(), 78 | kwargs=dict(server='testserver'), 79 | vals=dict(server='testserver'))), 80 | ('fanout_attr', dict(attrs=dict(fanout=True), 81 | kwargs=dict(), 82 | vals=dict(fanout=True))), 83 | ('fanout_arg', dict(attrs=dict(), 84 | kwargs=dict(fanout=True), 85 | vals=dict(fanout=True))), 86 | ] 87 | 88 | def test_callable(self): 89 | target = oslo_messaging.Target(**self.attrs) 90 | target = target(**self.kwargs) 91 | for k in self.vals: 92 | self.assertEqual(self.vals[k], getattr(target, k)) 93 | for k in ['exchange', 'topic', 'namespace', 94 | 'version', 'server', 'fanout']: 95 | if k in self.vals: 96 | continue 97 | self.assertIsNone(getattr(target, k)) 98 | 99 | 100 | class TargetReprTestCase(test_utils.BaseTestCase): 101 | 102 | scenarios = [ 103 | ('all_none', dict(kwargs=dict(), repr='')), 104 | ('exchange', dict(kwargs=dict(exchange='testexchange'), 105 | repr='exchange=testexchange')), 106 | ('topic', dict(kwargs=dict(topic='testtopic'), 107 | repr='topic=testtopic')), 108 | ('namespace', dict(kwargs=dict(namespace='testnamespace'), 109 | repr='namespace=testnamespace')), 110 | ('version', dict(kwargs=dict(version='3.4'), 111 | repr='version=3.4')), 112 | ('server', dict(kwargs=dict(server='testserver'), 113 | repr='server=testserver')), 114 | ('fanout', dict(kwargs=dict(fanout=True), 115 | repr='fanout=True')), 116 | ('exchange_and_fanout', dict(kwargs=dict(exchange='testexchange', 117 | fanout=True), 118 | repr='exchange=testexchange, ' 119 | 'fanout=True')), 120 | ] 121 | 122 | def test_repr(self): 123 | target = oslo_messaging.Target(**self.kwargs) 124 | self.assertEqual('', str(target)) 125 | 126 | 127 | _notset = object() 128 | 129 | 130 | class EqualityTestCase(test_utils.BaseTestCase): 131 | 132 | @classmethod 133 | def generate_scenarios(cls): 134 | attr = [ 135 | ('exchange', dict(attr='exchange')), 136 | ('topic', dict(attr='topic')), 137 | ('namespace', dict(attr='namespace')), 138 | ('version', dict(attr='version')), 139 | ('server', dict(attr='server')), 140 | ('fanout', dict(attr='fanout')), 141 | ] 142 | a = [ 143 | ('a_notset', dict(a_value=_notset)), 144 | ('a_none', dict(a_value=None)), 145 | ('a_empty', dict(a_value='')), 146 | ('a_foo', dict(a_value='foo')), 147 | ('a_bar', dict(a_value='bar')), 148 | ] 149 | b = [ 150 | ('b_notset', dict(b_value=_notset)), 151 | ('b_none', dict(b_value=None)), 152 | ('b_empty', dict(b_value='')), 153 | ('b_foo', dict(b_value='foo')), 154 | ('b_bar', dict(b_value='bar')), 155 | ] 156 | 157 | cls.scenarios = testscenarios.multiply_scenarios(attr, a, b) 158 | for s in cls.scenarios: 159 | s[1]['equals'] = (s[1]['a_value'] == s[1]['b_value']) 160 | 161 | def test_equality(self): 162 | a_kwargs = {self.attr: self.a_value} 163 | b_kwargs = {self.attr: self.b_value} 164 | 165 | a = oslo_messaging.Target(**a_kwargs) 166 | b = oslo_messaging.Target(**b_kwargs) 167 | 168 | if self.equals: 169 | self.assertEqual(a, b) 170 | self.assertFalse(a != b) 171 | else: 172 | self.assertNotEqual(a, b) 173 | self.assertFalse(a == b) 174 | 175 | 176 | EqualityTestCase.generate_scenarios() 177 | -------------------------------------------------------------------------------- /oslo_messaging/tests/test_utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2013 Red Hat, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | from oslo_messaging._drivers import common 15 | from oslo_messaging import _utils as utils 16 | from oslo_messaging.tests import utils as test_utils 17 | from unittest import mock 18 | 19 | 20 | class VersionIsCompatibleTestCase(test_utils.BaseTestCase): 21 | def test_version_is_compatible_same(self): 22 | self.assertTrue(utils.version_is_compatible('1.23', '1.23')) 23 | 24 | def test_version_is_compatible_newer_minor(self): 25 | self.assertTrue(utils.version_is_compatible('1.24', '1.23')) 26 | 27 | def test_version_is_compatible_older_minor(self): 28 | self.assertFalse(utils.version_is_compatible('1.22', '1.23')) 29 | 30 | def test_version_is_compatible_major_difference1(self): 31 | self.assertFalse(utils.version_is_compatible('2.23', '1.23')) 32 | 33 | def test_version_is_compatible_major_difference2(self): 34 | self.assertFalse(utils.version_is_compatible('1.23', '2.23')) 35 | 36 | def test_version_is_compatible_newer_rev(self): 37 | self.assertFalse(utils.version_is_compatible('1.23', '1.23.1')) 38 | 39 | def test_version_is_compatible_newer_rev_both(self): 40 | self.assertFalse(utils.version_is_compatible('1.23.1', '1.23.2')) 41 | 42 | def test_version_is_compatible_older_rev_both(self): 43 | self.assertTrue(utils.version_is_compatible('1.23.2', '1.23.1')) 44 | 45 | def test_version_is_compatible_older_rev(self): 46 | self.assertTrue(utils.version_is_compatible('1.24', '1.23.1')) 47 | 48 | def test_version_is_compatible_no_rev_is_zero(self): 49 | self.assertTrue(utils.version_is_compatible('1.23.0', '1.23')) 50 | 51 | 52 | class TimerTestCase(test_utils.BaseTestCase): 53 | def test_no_duration_no_callback(self): 54 | t = common.DecayingTimer() 55 | t.start() 56 | remaining = t.check_return() 57 | self.assertIsNone(remaining) 58 | 59 | def test_no_duration_but_maximum(self): 60 | t = common.DecayingTimer() 61 | t.start() 62 | remaining = t.check_return(maximum=2) 63 | self.assertEqual(2, remaining) 64 | 65 | @mock.patch('oslo_utils.timeutils.now') 66 | def test_duration_expired_no_callback(self, now): 67 | now.return_value = 0 68 | t = common.DecayingTimer(2) 69 | t.start() 70 | 71 | now.return_value = 3 72 | remaining = t.check_return() 73 | self.assertEqual(0, remaining) 74 | 75 | @mock.patch('oslo_utils.timeutils.now') 76 | def test_duration_callback(self, now): 77 | now.return_value = 0 78 | t = common.DecayingTimer(2) 79 | t.start() 80 | 81 | now.return_value = 3 82 | callback = mock.Mock() 83 | remaining = t.check_return(callback) 84 | self.assertEqual(0, remaining) 85 | callback.assert_called_once_with() 86 | 87 | @mock.patch('oslo_utils.timeutils.now') 88 | def test_duration_callback_with_args(self, now): 89 | now.return_value = 0 90 | t = common.DecayingTimer(2) 91 | t.start() 92 | 93 | now.return_value = 3 94 | callback = mock.Mock() 95 | remaining = t.check_return(callback, 1, a='b') 96 | self.assertEqual(0, remaining) 97 | callback.assert_called_once_with(1, a='b') 98 | 99 | @mock.patch('oslo_utils.timeutils.now') 100 | def test_reset(self, now): 101 | now.return_value = 0 102 | t = common.DecayingTimer(3) 103 | t.start() 104 | 105 | now.return_value = 1 106 | remaining = t.check_return() 107 | self.assertEqual(2, remaining) 108 | 109 | t.restart() 110 | remaining = t.check_return() 111 | self.assertEqual(3, remaining) 112 | -------------------------------------------------------------------------------- /oslo_messaging/tests/utils.py: -------------------------------------------------------------------------------- 1 | # Copyright 2010-2011 OpenStack Foundation 2 | # Copyright 2010 United States Government as represented by the 3 | # Administrator of the National Aeronautics and Space Administration. 4 | # Copyright 2013 Hewlett-Packard Development Company, L.P. 5 | # All Rights Reserved. 6 | # Copyright 2013 Red Hat, Inc. 7 | # 8 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 9 | # not use this file except in compliance with the License. You may obtain 10 | # a copy of the License at 11 | # 12 | # http://www.apache.org/licenses/LICENSE-2.0 13 | # 14 | # Unless required by applicable law or agreed to in writing, software 15 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 16 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 17 | # License for the specific language governing permissions and limitations 18 | # under the License. 19 | 20 | """Common utilities used in testing""" 21 | 22 | import threading 23 | 24 | from oslo_config import cfg 25 | from oslo_context.context import RequestContext 26 | from oslo_utils import eventletutils 27 | from oslotest import base 28 | 29 | 30 | TRUE_VALUES = ('true', '1', 'yes') 31 | 32 | 33 | class BaseTestCase(base.BaseTestCase): 34 | 35 | def setUp(self, conf=cfg.CONF): 36 | super().setUp() 37 | 38 | from oslo_messaging import conffixture 39 | self.messaging_conf = self.useFixture(conffixture.ConfFixture(conf)) 40 | self.messaging_conf.transport_url = 'fake:/' 41 | self.conf = self.messaging_conf.conf 42 | 43 | self.conf.project = 'project' 44 | self.conf.prog = 'prog' 45 | 46 | def config(self, **kw): 47 | """Override some configuration values. 48 | 49 | The keyword arguments are the names of configuration options to 50 | override and their values. 51 | 52 | If a group argument is supplied, the overrides are applied to 53 | the specified configuration option group. 54 | 55 | All overrides are automatically cleared at the end of the current 56 | test by the tearDown() method. 57 | """ 58 | group = kw.pop('group', None) 59 | for k, v in kw.items(): 60 | self.conf.set_override(k, v, group) 61 | 62 | 63 | class ServerThreadHelper(threading.Thread): 64 | def __init__(self, server): 65 | super().__init__() 66 | self.daemon = True 67 | self._server = server 68 | self._stop_event = eventletutils.Event() 69 | self._start_event = eventletutils.Event() 70 | 71 | def start(self): 72 | super().start() 73 | self._start_event.wait() 74 | 75 | def run(self): 76 | self._server.start() 77 | self._start_event.set() 78 | self._stop_event.wait() 79 | # Check start() does nothing with a running listener 80 | self._server.start() 81 | self._server.stop() 82 | self._server.wait() 83 | 84 | def stop(self): 85 | self._stop_event.set() 86 | 87 | 88 | class TestContext(RequestContext): 89 | def redacted_copy(self): 90 | # NOTE(JayF): By returning our self here instead of redacting, we can 91 | # continue using equality comparisons in unit tests. 92 | return self 93 | -------------------------------------------------------------------------------- /oslo_messaging/version.py: -------------------------------------------------------------------------------- 1 | # Copyright 2016 OpenStack Foundation 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you may 4 | # not use this file except in compliance with the License. You may obtain 5 | # a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 11 | # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 12 | # License for the specific language governing permissions and limitations 13 | # under the License. 14 | 15 | 16 | import pbr.version 17 | 18 | version_info = pbr.version.VersionInfo('oslo.messaging') 19 | -------------------------------------------------------------------------------- /releasenotes/notes/RPC-call-monitoring-7977f047d069769a.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | RPCClient now supports RPC call monitoring for detecting the loss 4 | of a server during an RPC call. 5 | features: 6 | - | 7 | RPC call monitoring is a new RPCClient feature. Call monitoring 8 | causes the RPC server to periodically send keepalive messages back 9 | to the RPCClient while the RPC call is being processed. This can 10 | be used for early detection of a server failure without having to 11 | wait for the full call timeout to expire. 12 | -------------------------------------------------------------------------------- /releasenotes/notes/add-enable_cancel_on_failover-22ac472b93dd3a23.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Add a new option `enable_cancel_on_failover` for rabbitmq driver 5 | which when enabled, will cancel consumers when queue appears 6 | to be down. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/add-ping-endpoint.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | RPC dispatcher can have an extra endpoint named ping. 5 | This endpoint can be enabled thanks to a specific configuration parameter: 6 | [DEFAULT] 7 | rpc_ping_enabled=true # default is false 8 | 9 | The purpose of this new endpoint is to help operators do a RPC call (a 10 | ping) toward a specific RPC callback (e.g. a nova-compute, or a 11 | neutron-agent). 12 | This is helping a lot for monitoring agents (for example, if agents are 13 | deployed in a kubernetes pod). 14 | -------------------------------------------------------------------------------- /releasenotes/notes/add-quorum-control-configurations-beed79811ff97ba2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add quorum configuration x-max-in-memory-length, 5 | x-max-in-memory-bytes, x-delivery-limit which control the quorum 6 | queue memory usage and handle the message poisoning problem 7 | -------------------------------------------------------------------------------- /releasenotes/notes/add-ssl-support-for-kafka.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | | SSL support for oslo_messaging's kafka driver 5 | | Next configuration params was added 6 | 7 | * *ssl_client_cert_file* (default='') 8 | * *ssl_client_key_file* (default='') 9 | * *ssl_client_key_password* (default='') 10 | -------------------------------------------------------------------------------- /releasenotes/notes/add_reno-3b4ae0789e9c45b4.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | other: 3 | - Switch to reno for managing release notes. -------------------------------------------------------------------------------- /releasenotes/notes/adding_support_for_quorum_queues-3101d055b492289e.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adding support for quorum queues. Quorum queues are enabled if the 5 | ``rabbit_quorum_queue`` parameter is sets (``x-queue-type: quorum``). 6 | Setting x-queue-type to quorum means that replicated FIFO queue based on 7 | the Raft consensus algorithm will be used. It is available as of 8 | RabbitMQ 3.8.0. The quorum queues are durable by default 9 | (``amqp_durable_queues``) will be ignored. 10 | when enabled the HA queues (``rabbit_ha_queues``) aka mirrored queues 11 | should be disabled since the queue can't be both types at the same time 12 | -------------------------------------------------------------------------------- /releasenotes/notes/allow-transient-no-expire-ce7ae9d8c9d15751.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Allow creation of transient queues with no expire. 5 | When an operator rely on rabbitmq policies, there is no point to set the 6 | queue TTL in config. 7 | When the rabbit_transient_queues_ttl is set to 0, no x-expire parameter 8 | will be set on queue declaration. 9 | In that specific situation, it is recommended to set an expire value using 10 | rabbitmq policies. 11 | See https://www.rabbitmq.com/parameters.html#policies 12 | -------------------------------------------------------------------------------- /releasenotes/notes/auto-deleted-failed-quorum-ca6a3923c3ed999a.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Auto-delete the failed quorum rabbit queues. 5 | When rabbit is failing for a specific quorum queue, delete the queue 6 | before trying to recreate it. 7 | This may happen if the queue is not recoverable on rabbit side. 8 | See https://www.rabbitmq.com/quorum-queues.html#availability for more 9 | info on this specific case. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/blocking-executor-deprecated-895146c1c3bf2f51.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - The blocking executor has been deprecated for removal in Rocky. Its usage 4 | was never recommended for applications, and it has no test coverage. 5 | Applications should choose the appropriate threading model that maps their 6 | usage instead. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/blocking-executor-support-dropped-a3bc74c6825863f0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The blocking executor has been deprecated for removal in Rocky and support 5 | is now dropped in Ussuri. Its usage was never recommended for applications, 6 | and it has no test coverage. 7 | Applications should choose the appropriate threading model that maps to 8 | their usage instead. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-1917645-rabbit-use-retry-parameter-for-notifications-3f7c508ab4437579.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | As a fix for `bug 1917645 `_ the rabbit 5 | backend is changed to use the ``[oslo_messaging_notifications]retry`` 6 | parameter when driver tries to connect to the message bus during 7 | notification sending. Before this fix the rabbit backend retried the 8 | connection forever blocking the caller thread. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-1981093-kafka-dont-log-in-tpool-execute-fa50ceee2d55ebae.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | [`bug 1981093 `_] 5 | Pulls calls to logging functions out of ``impl_kafka._produce_message``. 6 | Since ``_produce_message`` is called through tpool.execute, calling logging 7 | functions inside ``_produce_message`` could cause subsequent calls to 8 | logging functions to deadlock. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-1993149-e8b231791b65e938.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | If kombu_reconnect_delay is specified in the [oslo_messaging_rabbit] section, 5 | ensure that it is less than 5.0, the value of ACK_REQUEUE_EVERY_SECONDS_MAX 6 | fixes: 7 | - | 8 | Increased ACK_REQUEUE_EVERY_SECONDS_MAX to resolve issues with rabbitmq HA 9 | failover. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-2068630-6ff92f213bc4eca0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Force queue deletion when it is not possible de redeclare a queue. 5 | See `bug 2068630 `__ 6 | for details. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/bug-2098714-d55094fa4fbb3178.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixes delayed metrics processing in services using eventlet, caused by 5 | mixing a native thread with an eventlet-patched queue. 6 | See `bug 2098714 `__ 7 | for details. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/bump-amqp-version-due-to-tls-issue-e877b152eb101c15.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | critical: 3 | - | 4 | In combination with amqp<=2.4.0, ``oslo.messaging`` was unreliable 5 | when configured with TLS (as is generally recommended). Users would 6 | see frequent errors such as this:: 7 | 8 | MessagingTimeout: Timed out waiting for a reply to message ID ae039d1695984addbfaaef032ce4fda3 9 | 10 | Such issues would typically lead to downstream service timeouts, 11 | with no recourse available other than disabling TLS altogether 12 | (see `bug 1800957 13 | `_). 14 | 15 | The underlying issue is fixed in amqp version 2.4.1, which is now 16 | the minimum version that ``oslo.messaging`` requires. 17 | -------------------------------------------------------------------------------- /releasenotes/notes/connection_ttl-2cf0fe6e1ab8c73c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | | Idle connections in the pool will be expired and closed. 5 | | Default ttl is 1200s. Next configuration params was added 6 | 7 | * *conn_pool_ttl* (defaul 1200) 8 | * *conn_pool_min_size* (default 2) 9 | -------------------------------------------------------------------------------- /releasenotes/notes/declare_fallback_durable_exchange-0db677de4fdf1e78.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Force creating non durable control exchange when a precondition failed 5 | related to config that differ occuring. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-ZeroMQ-driver-a8af25aaba867c5b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | ZeroMQ support has been deprecated. The ZeroMQ driver ``zmq://`` has 5 | been unmaintained for over a year and no longer functions 6 | properly. It is recommended to use one of the maintained backends 7 | instead, such as RabbitMQ or AMQP 1.0. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-eventlet-executor-13835b9818fd77f2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | Eventlet usages are deprecated and the removal of Eventlet from 5 | OpenStack `is planned `_, for this reason the Eventlet executor is 6 | deprecated. Start migrating your stack to the threading executor. 7 | Please also start considering removing your internal Eventlet usages. 8 | - | 9 | The `executor` parameter of the `MessageHandlingServer` class is now 10 | deprecated and planned for removal. The Eventlet executor is deprecated. 11 | Only the threading executor will remains available so the `executor` 12 | parameter is useless. 13 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecate-the-option-heartbeat_in_pthread-from-rabbit-driver-5757adb83701caa5.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | The ``heartbeat_in_pthread`` option from the rabbitmq driver has been 5 | deprecated and it is recommended not to use the feature anymore. 6 | The option is strongly related to Eventlet but Eventlet will be removed 7 | from OpenStack services in a future release. In addition, this feature has 8 | never worked with services using eventlet for core service framework. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/deprecated-amqp1-driver-4bf57449bc2b7aad.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - | 4 | The AMQP1 driver is now deprecated. Its related functional tests are also 5 | disabled. Neither debian nor ubuntu in the latest releases have any binary 6 | built for qpid server, not even 3rd party. Only qpid proton, the client 7 | lib, is available. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/disable-mandatory-flag-a6210a534f3853f0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Deprecating the ``direct_mandatory_flag``. It will not be possible to 5 | deactivate this functionality anymore. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/do-not-run-heartbeat-in-pthread-by-default-42e1299f59b841f8.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The ``[oslo_messaging_rabbit] heartbeat_in_pthread`` config option 5 | defaults to ``False`` again. 6 | For wsgi applications it is recommended to set this value to ``True`` 7 | but enabling it for non-wsgi services may break such service. 8 | Please check https://bugs.launchpad.net/oslo.messaging/+bug/1934937 9 | for more details. 10 | -------------------------------------------------------------------------------- /releasenotes/notes/drop-python27-support-5ef2f365d8930483.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Support for Python 2.7 has been dropped. The minimum version of Python now 5 | supported is Python 3.6. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/enforce_fips_mode-07dd259eb8a73c2b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adding a new option, ``[oslo_messaging_rabbit] ssl_enforce_fips_mode``, to 5 | the rabbitmq driver to enforce the OpenSSL FIPS mode if supported by the 6 | version of Python. 7 | security: 8 | - | 9 | We are now able to enforce the OpenSSL FIPS mode by using 10 | ``[oslo_messaging_rabbit] ssl_enforce_fips_mode``. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/fix-access_policy-deafult-a6954a147cb002b0.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Change the default value of RPC dispatcher access_policy 5 | to DefaultRPCAccessPolicy. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/get-rpc-client-0b4aa62160864b29.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Added new ``get_rpc_client`` function to instantiate the RPCClient 5 | class 6 | deprecations: 7 | - | 8 | Instantiating the RPCClient class directly is deprecated in favor 9 | of using the new ``get_rpc_client`` function to expose a more 10 | common API similar to existing functions such as ``get_rpc_server`` 11 | and ``get_rpc_transport`` 12 | -------------------------------------------------------------------------------- /releasenotes/notes/get-rpc-helpers-cls-8911826ac08aef2a.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | The ``get_rpc_transport``, ``get_rpc_server`` and ``get_rpc_client`` helper 5 | functions now have support for overriding the class that is instantiated. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/get_rpc_transport-4aa3511ad9754a60.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add get_rpc_transport call to make the API clear for the separation 5 | of RPC and Notification messaging backends. 6 | deprecations: 7 | - | 8 | Deprecate get_transport and use get_rpc_transport or 9 | get_notification_transport to make the API usage clear for the 10 | separation of RPC and Notification messaging backends. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/handle-missing-queue-553a803f94976be7.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Adding retry strategy based on the mandatory flag. Missing exchanges and 5 | queues are now identified separately for logging purposes. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/heartbeat-rate-3-7ada9edbccc11a3f.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Change heartbeat_rate default from 2 to 3 in order to send AMQP heartbeat 5 | frames at correct interval 6 | -------------------------------------------------------------------------------- /releasenotes/notes/kafka-client-library-change-fe16d5a34550db7f.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Threading issues with the kafka-python consumer client were identified 5 | and documented. The driver has been updated to integrate the 6 | confluent-kafka python library. The confluent-kafka client 7 | leverages the high performance librdkafka C client and is safe 8 | for multiple thread use. 9 | upgrade: 10 | - | 11 | With the change in the client library used, projects using the 12 | Kafka driver should use extras oslo.messaging[kafka] to pull in 13 | dependencies for the driver. 14 | -------------------------------------------------------------------------------- /releasenotes/notes/kombo-reconnect-splay-a81eb5fca6180510.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add a new option named `kombu_reconnect_splay` under 5 | `oslo_messaging_rabbit` that could be used to add an extra random delay 6 | before any reconnection when a recoverable error occur. 7 | This delay is set to 0 by default so the original behavior is not changed. 8 | -------------------------------------------------------------------------------- /releasenotes/notes/no-log-if-ignore-errors-e2223b8a646b4c40.yaml: -------------------------------------------------------------------------------- 1 | other: 2 | - | 3 | NoSuchMethod exception will not be logged for special non-existing methods 4 | which names end with '_ignore_errors'. Such methods might be used 5 | as health probes for openstack services. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/option-rabbitmq-max_retries-has-been-deprecated-471f66a9e6d672a2.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - The rabbitmq driver option ``DEFAULT/max_retries`` has been deprecated 4 | for removal (at a later point in the future) as it did not make logical 5 | sense for notifications and for RPC. 6 | -------------------------------------------------------------------------------- /releasenotes/notes/oslo-metrics-support-fe16343a637cc14b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | | Introduce support for sending rpc client metrics to oslo.metrics. 5 | | This feature can be enabled by setting a configuration parameter: 6 | 7 | [oslo_messaging_metrics] 8 | metrics_enabled = True # default is false 9 | -------------------------------------------------------------------------------- /releasenotes/notes/pika-driver-has-been-deprecated-e2407fa53c91fe5c.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deprecations: 3 | - The pika driver has been deprecated for removal in Rocky. This 4 | driver was developed as a replacement for the default rabbit 5 | driver. However testing has not shown any appreciable improvement 6 | over the default rabbit driver in terms of performance and 7 | stability. 8 | 9 | -------------------------------------------------------------------------------- /releasenotes/notes/rabbit-no-wait-for-ack-9e5de3e1320d7660.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | other: 3 | - | 4 | On rabbitmq, in the past, acknownlegement of messages was done within the 5 | application callback thread/greenlet. This thread was blocked until the 6 | message was ack. In newton, we rewrote the message acknownlegement to 7 | ensure we haven't two threads writting the socket at the same times. 8 | Now all pendings ack are done by the main thread. They are no more reason 9 | to block the application callback thread until the message is ack. Other 10 | driver already release the application callback threads before the message 11 | is acknownleged. This is also the case for rabbitmq, now. 12 | 13 | -------------------------------------------------------------------------------- /releasenotes/notes/rabbit_queue_manager-363209285cbbe257.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add three new options (``use_queue_manager``, ``hostname``, 5 | ``processname``) to switch oslo.messaging from random queue names 6 | (for reply_q and fanouts) to consistent naming. 7 | The default value is False, so oslo.messaging will still use random queue 8 | names if nothing is set in configuration file of services. 9 | When switching use_queue_manager to True, the uuid4 random string from the 10 | queue name is replaced with a combination of hostname, processname and 11 | counter. 12 | The counter will be kept in shared memory (/dev/shm/x_y_qmanager). 13 | This way, when a service using oslo.messaging restarts (e.g. neutron), 14 | it will re-create the queues using the same name as the previous run, so 15 | no new queues are created and no need for rabbitmq to delete the previous 16 | queues. 17 | This is extremely useful for operator to debug which queue belong to which 18 | server/process. 19 | It's also higlhy recommended to enable this feature when using quorum 20 | queues for transient (option named ``rabbit_transient_quorum_queue``) to 21 | avoid consuming all erlang atoms after some time. 22 | -------------------------------------------------------------------------------- /releasenotes/notes/rabbit_quorum_typo-9c06a9fd8d767f53.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | fixes: 3 | - | 4 | Fixed typo in variable names ``rabbit_quorum_max_memory_length`` 5 | and ``rabbit_quorum_max_memory_bytes``. Please make changes in your 6 | config files to correspond correct variables. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/rabbit_transient_quorum-fc3c3f88ead90034.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add an option to enable transient queues to use quorum. 5 | 6 | Transient queues in OpenStack are not so transient, they live the whole 7 | process lifetime (e.g. until you restart a service, like nova-compute). 8 | Transient here means they belong to a specific process, compared to 9 | regular queues which may be used by more processes. 10 | Usually, transients queues are the "fanout" and "reply" queues. 11 | 12 | By default, without any rabbitmq policy tuning, they are not durable 13 | neither highly available. 14 | 15 | By enabling quorum for transients, oslo.messaging will declare quorum 16 | queues instead of classic on rabbitmq. As a result, those queues will 17 | automatically become HA and durable. 18 | Note that this may have an impact on your cluster, as rabbit will need 19 | more cpu, ram and network bandwith to manage the queues. This was tested 20 | at pretty large scale (2k hypervisors) with a cluster of 5 nodes. 21 | 22 | Also note that the current rabbitmq implementation rely on a fixed number 23 | of "erlang atom" (5M by default), and one atom is consumed each time a 24 | quorum queue is created with a different name. If your deployment is doing 25 | a lot of queue deletion/creation, you may consume all your atoms quicker. 26 | 27 | When enabling quorum for transients, you may also want to update your 28 | rabbitmq policies accordingly (e.g. make sure they apply on quorum). 29 | 30 | This option will stay disabled by default for now but may become the 31 | default in the future. 32 | -------------------------------------------------------------------------------- /releasenotes/notes/rabbitmq-opts-cleanup-e0f97d4cc0855c5a.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The deprecated ``[oslo_messaging_rabbit] rabbit_use_ssl`` option has been 5 | removed. Use the ``ssl`` option instead. 6 | 7 | - | 8 | The following deprecated options in ``[oslo_messaging_rabbit]`` section 9 | have been removed. Use the ones without ``kombu_`` prefix. 10 | 11 | - ``kombu_ssl_version`` 12 | - ``kombu_ssl_keyfile`` 13 | - ``kombu_ssl_certfile`` 14 | - ``kombu_ssl_ca_certs`` 15 | 16 | - | 17 | The following options are no longer loaded from the ``[DEFAULT]`` section. 18 | Use the ``[oslo_messaging_rabbit]`` section instead. 19 | 20 | - ``amqp_auto_delete`` 21 | - ``kombu_reconnect_delay`` 22 | - ``rabbit_login_method`` 23 | - ``rabbit_retry_backoff`` 24 | - ``rabbit_ha_queues`` 25 | - ``rpc_conn_pool_size`` 26 | -------------------------------------------------------------------------------- /releasenotes/notes/removal-deprecated-options-6d4c5db90525c52d.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Remove deprecated configuration options from multiple drivers. 5 | 6 | * The rpc_backend option from the [DEFAULT] section has been removed. 7 | * The AMQP driver has removed the configuration options of allow_insecure_clients, username and password from the [oslo_messaging_amqp] section. 8 | * The Kafa driver has removed the configuration options of kafka_default_host and kafka_default_port from the [oslo_messaging_kafka] section. 9 | * The Rabbit driver has removed the configuration options of rabbit_host, rabbit_port, rabbit_hosts, rabbit_userid, rabbit_password, rabbit_virtual_host rabbit_max_retries and rabbit_durable_queues from the [oslo_messaging_rabbit] section. 10 | 11 | Operators must switch to setting the transport_url directive in the [DEFAULT] section. 12 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-RequestContextSerializer-234c0496a7e0376b.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - RequestContextSerializer was deprecated since 4.6, and it 4 | isn't used by any other project, so we can remove it safely. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-ZeroMQ-driver-e9e0bbbb7bd4f5e6.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | The ZMQ-based driver for RPC communications has been removed 4 | deprecations: 5 | - | 6 | The driver support for the ZeroMQ messaging library is removed. 7 | Users of the oslo.messaging RPC services must use the supported 8 | rabbit ("rabbit://...") or amqp1 ("amqp://..." )drivers. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-amqp1-c924ea548dadffaa.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The AMQP1 driver, which was deprecated in 14.1.0 was removed, due to 5 | limited usage and lack of support on recent distributions. Use any of 6 | the other supported driver, such as RabbitMQ or Kafka. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-deprecated-notif-opts-142f8eea540c17ec.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The following deprecated options have been removed. Use the equivalent 5 | options in the ``[oslo_messaging_notifications]`` secion instead. 6 | 7 | - ``[DEFAULT] notification_driver`` 8 | - ``[DEFAULT] notification_transport_url`` 9 | - ``[DEFAULT] notification_topics`` 10 | - ``[DEFAULT] routing_config`` 11 | 12 | - | 13 | The deprecated ``[rpc_notifier2] topics`` option has been removed. Use 14 | the ``[oslo_messaging_notifications] topics`` option instead. 15 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-kafka-conn-pool-opts-0b7962e2f22b24ed.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The following deprecated options in the ``[oslo_messaging_kafka]`` section 5 | have been removed. 6 | 7 | - ``pool_size`` 8 | - ``conn_pool_min_size`` 9 | - ``conn_pool_ttl`` 10 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-old-quorum-opts-with-typo-5e013064fb6df062.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The following options in ``[oslo_messaging_rabbit]`` secion have been 5 | removed. 6 | 7 | - ``rabbit_quroum_max_memory_length`` 8 | - ``rabbit_quroum_max_memory_bytes`` 9 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-pika-1bae204ced2521a3.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | The Pika-based driver for RabbitMQ has been removed. 4 | upgrade: 5 | - | 6 | Users of the Pika-based driver must change the prefix of all the 7 | transport_url configuration options from "pika://..." to 8 | "rabbit://..." to use the default kombu based RabbitMQ driver. 9 | -------------------------------------------------------------------------------- /releasenotes/notes/remove-py38-381f832001230756.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | Support for Python 3.8 has been removed. Now the minimum python version 5 | supported is 3.9 . 6 | -------------------------------------------------------------------------------- /releasenotes/notes/reply_q-timeout-e3c3bae636e8bc74.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | The name of the ``reply_q`` is now logged when a timeout occurs while waiting for a reply. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/retry-support-07996ef04dda9482.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | | Retry support for oslo_messaging_notifications driver 5 | | Configuration param 'retry' is added. Default is -1, indefinite 6 | 7 | * *retry* (default=-1) 8 | 9 | -------------------------------------------------------------------------------- /releasenotes/notes/run-heartbeat-in-pthread-by-default-28637b41ebf500dc.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | The ``[oslo_messaging_rabbit] heartbeat_in_pthread`` config option now 5 | defaults to ``True``. 6 | Applications will run RabbitMQ heartbeat in a Python thread by default. 7 | deprecations: 8 | - | 9 | ``heartbeat_in_pthread`` has been deprecated and will be removed in a 10 | future release. If configured, this option should be unset. 11 | -------------------------------------------------------------------------------- /releasenotes/notes/stream-c3dd31ee98f6bbd7.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | features: 3 | - | 4 | Add an option to use stream queues for rabbitmq driver instead of fanouts. 5 | -------------------------------------------------------------------------------- /releasenotes/notes/undeprecate_heartbeat_in_pthread-48e2c1fc008cf208.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | upgrade: 3 | - | 4 | We undeprecated the ``heartbeat_in_pthread`` option. This option will 5 | remain available to allow customers to run the rabbitmq heartbeat in 6 | python thread or not. 7 | -------------------------------------------------------------------------------- /releasenotes/notes/use-extras-for-optional-deps-2a00e8007ef7a629.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | prelude: > 3 | Projects using any of the optional drivers can use extras to 4 | pull in dependencies for that driver. 5 | upgrade: 6 | - | 7 | Projects using the AMQP 1.0 driver may now depend on 8 | oslo.messaging[amqp1]. Projects using the Kafka driver may now 9 | depend on oslo.messaging[kafka] 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /releasenotes/source/2023.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2023.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/2023.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/2023.2.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2023.2 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2023.2 7 | -------------------------------------------------------------------------------- /releasenotes/source/2024.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2024.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2024.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/2024.2.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2024.2 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2024.2 7 | -------------------------------------------------------------------------------- /releasenotes/source/2025.1.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | 2025.1 Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/2025.1 7 | -------------------------------------------------------------------------------- /releasenotes/source/_static/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/releasenotes/source/_static/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/_templates/.placeholder: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/openstack/oslo.messaging/510688d8ddc807242fe9a36532dd03ba3c2d4ad5/releasenotes/source/_templates/.placeholder -------------------------------------------------------------------------------- /releasenotes/source/conf.py: -------------------------------------------------------------------------------- 1 | # Licensed under the Apache License, Version 2.0 (the "License"); 2 | # you may not use this file except in compliance with the License. 3 | # You may obtain a copy of the License at 4 | # 5 | # http://www.apache.org/licenses/LICENSE-2.0 6 | # 7 | # Unless required by applicable law or agreed to in writing, software 8 | # distributed under the License is distributed on an "AS IS" BASIS, 9 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 10 | # implied. 11 | # See the License for the specific language governing permissions and 12 | # limitations under the License. 13 | 14 | # oslo.log Release Notes documentation build configuration file, created by 15 | # sphinx-quickstart on Tue Nov 3 17:40:50 2015. 16 | # 17 | # This file is execfile()d with the current directory set to its 18 | # containing dir. 19 | # 20 | # Note that not all possible configuration values are present in this 21 | # autogenerated file. 22 | # 23 | # This file does only contain a selection of the most common options. For a 24 | # full list see the documentation: 25 | # http://www.sphinx-doc.org/en/master/config 26 | 27 | # -- Project information -------------------------------------------------- 28 | # General information about the project. 29 | copyright = '2016, oslo.messaging Developers' 30 | 31 | # Release notes do not need a version in the title, they span 32 | # multiple versions. 33 | # The full version, including alpha/beta/rc tags. 34 | release = '' 35 | # The short X.Y version. 36 | version = '' 37 | 38 | # -- General configuration ------------------------------------------------ 39 | 40 | # Add any Sphinx extension module names here, as strings. They can be 41 | # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom ones. 42 | extensions = [ 43 | 'openstackdocstheme', 44 | 'reno.sphinxext', 45 | ] 46 | 47 | # openstackdocstheme options 48 | openstackdocs_repo_name = 'openstack/oslo.messaging' 49 | openstackdocs_bug_project = 'oslo.messaging' 50 | openstackdocs_bug_tag = '' 51 | 52 | # The master toctree document. 53 | master_doc = 'index' 54 | 55 | # -- Options for HTML output ---------------------------------------------- 56 | 57 | # The theme to use for HTML and HTML Help pages. See the documentation for 58 | # a list of builtin themes. 59 | html_theme = 'openstackdocs' 60 | 61 | # -- Options for Internationalization output ------------------------------ 62 | locale_dirs = ['locale/'] 63 | -------------------------------------------------------------------------------- /releasenotes/source/index.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | oslo.messaging Release Notes 3 | ============================= 4 | 5 | .. toctree:: 6 | :maxdepth: 1 7 | 8 | unreleased 9 | 2025.1 10 | 2024.2 11 | 2024.1 12 | 2023.2 13 | 2023.1 14 | zed 15 | yoga 16 | xena 17 | wallaby 18 | victoria 19 | ussuri 20 | train 21 | stein 22 | rocky 23 | queens 24 | pike 25 | ocata 26 | newton 27 | -------------------------------------------------------------------------------- /releasenotes/source/newton.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Newton Series Release Notes 3 | ============================ 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/newton 7 | -------------------------------------------------------------------------------- /releasenotes/source/ocata.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Ocata Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: origin/stable/ocata 7 | -------------------------------------------------------------------------------- /releasenotes/source/pike.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Pike Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/pike 7 | -------------------------------------------------------------------------------- /releasenotes/source/queens.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Queens Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/queens 7 | -------------------------------------------------------------------------------- /releasenotes/source/rocky.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Rocky Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/rocky 7 | -------------------------------------------------------------------------------- /releasenotes/source/stein.rst: -------------------------------------------------------------------------------- 1 | =================================== 2 | Stein Series Release Notes 3 | =================================== 4 | 5 | .. release-notes:: 6 | :branch: stable/stein 7 | -------------------------------------------------------------------------------- /releasenotes/source/train.rst: -------------------------------------------------------------------------------- 1 | ========================== 2 | Train Series Release Notes 3 | ========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/train 7 | -------------------------------------------------------------------------------- /releasenotes/source/unreleased.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Current Series Release Notes 3 | ============================= 4 | 5 | .. release-notes:: 6 | -------------------------------------------------------------------------------- /releasenotes/source/ussuri.rst: -------------------------------------------------------------------------------- 1 | =========================== 2 | Ussuri Series Release Notes 3 | =========================== 4 | 5 | .. release-notes:: 6 | :branch: stable/ussuri 7 | -------------------------------------------------------------------------------- /releasenotes/source/victoria.rst: -------------------------------------------------------------------------------- 1 | ============================= 2 | Victoria Series Release Notes 3 | ============================= 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/victoria 7 | -------------------------------------------------------------------------------- /releasenotes/source/wallaby.rst: -------------------------------------------------------------------------------- 1 | ============================ 2 | Wallaby Series Release Notes 3 | ============================ 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/wallaby 7 | -------------------------------------------------------------------------------- /releasenotes/source/xena.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Xena Series Release Notes 3 | ========================= 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/xena 7 | -------------------------------------------------------------------------------- /releasenotes/source/yoga.rst: -------------------------------------------------------------------------------- 1 | ========================= 2 | Yoga Series Release Notes 3 | ========================= 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/yoga 7 | -------------------------------------------------------------------------------- /releasenotes/source/zed.rst: -------------------------------------------------------------------------------- 1 | ======================== 2 | Zed Series Release Notes 3 | ======================== 4 | 5 | .. release-notes:: 6 | :branch: unmaintained/zed 7 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | # Requirements lower bounds listed here are our best effort to keep them up to 2 | # date but we do not test them so no guarantee of having them all correct. If 3 | # you find any incorrect lower bounds, let us know or propose a fix. 4 | 5 | pbr>=2.0.0 # Apache-2.0 6 | 7 | futurist>=1.2.0 # Apache-2.0 8 | oslo.config>=5.2.0 # Apache-2.0 9 | oslo.context>=5.3.0 # Apache-2.0 10 | oslo.log>=3.36.0 # Apache-2.0 11 | oslo.utils>=3.37.0 # Apache-2.0 12 | oslo.serialization>=2.18.0 # Apache-2.0 13 | oslo.service>=1.24.0 # Apache-2.0 14 | stevedore>=1.20.0 # Apache-2.0 15 | debtcollector>=1.2.0 # Apache-2.0 16 | 17 | # for jsonutils 18 | cachetools>=2.0.0 # MIT License 19 | 20 | WebOb>=1.7.1 # MIT 21 | 22 | # for the routing notifier 23 | PyYAML>=3.13 # MIT 24 | 25 | # rabbit driver is the default 26 | # we set the amqp version to ensure heartbeat works 27 | amqp>=2.5.2 # BSD 28 | kombu>=4.6.6 # BSD 29 | 30 | # middleware 31 | oslo.middleware>=3.31.0 # Apache-2.0 32 | 33 | # metrics 34 | oslo.metrics>=0.2.1 # Apache-2.0 35 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | name = oslo.messaging 3 | author = OpenStack 4 | author_email = openstack-discuss@lists.openstack.org 5 | summary = Oslo Messaging API 6 | description_file = 7 | README.rst 8 | home_page = https://docs.openstack.org/oslo.messaging/latest/ 9 | python_requires = >=3.9 10 | classifier = 11 | Environment :: OpenStack 12 | Intended Audience :: Developers 13 | Intended Audience :: Information Technology 14 | License :: OSI Approved :: Apache Software License 15 | Operating System :: OS Independent 16 | Programming Language :: Python 17 | Programming Language :: Python :: 3 18 | Programming Language :: Python :: 3.9 19 | Programming Language :: Python :: 3.10 20 | Programming Language :: Python :: 3.11 21 | Programming Language :: Python :: 3.12 22 | Programming Language :: Python :: 3 :: Only 23 | Programming Language :: Python :: Implementation :: CPython 24 | 25 | [extras] 26 | # package dependencies for optional (non-rabbitmq) messaging drivers. 27 | # projects can test-depend on oslo.messaging[] 28 | # e.g.: oslo.messaging[kafka] 29 | kafka = 30 | confluent-kafka>=1.3.0 # Apache-2.0 31 | 32 | [files] 33 | packages = 34 | oslo_messaging 35 | 36 | [entry_points] 37 | console_scripts = 38 | oslo-messaging-send-notification = oslo_messaging.notify.notifier:_send_notification 39 | 40 | oslo.messaging.drivers = 41 | rabbit = oslo_messaging._drivers.impl_rabbit:RabbitDriver 42 | 43 | # This driver is supporting for only notification usage 44 | kafka = oslo_messaging._drivers.impl_kafka:KafkaDriver 45 | 46 | # To avoid confusion 47 | kombu = oslo_messaging._drivers.impl_rabbit:RabbitDriver 48 | 49 | # This is just for internal testing 50 | fake = oslo_messaging._drivers.impl_fake:FakeDriver 51 | 52 | oslo.messaging.executors = 53 | eventlet = futurist:GreenThreadPoolExecutor 54 | threading = futurist:ThreadPoolExecutor 55 | 56 | oslo.messaging.notify.drivers = 57 | messagingv2 = oslo_messaging.notify.messaging:MessagingV2Driver 58 | messaging = oslo_messaging.notify.messaging:MessagingDriver 59 | log = oslo_messaging.notify._impl_log:LogDriver 60 | test = oslo_messaging.notify._impl_test:TestDriver 61 | noop = oslo_messaging.notify._impl_noop:NoOpDriver 62 | routing = oslo_messaging.notify._impl_routing:RoutingDriver 63 | 64 | oslo.config.opts = 65 | oslo.messaging = oslo_messaging.opts:list_opts 66 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2013 Hewlett-Packard Development Company, L.P. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. 13 | # See the License for the specific language governing permissions and 14 | # limitations under the License. 15 | 16 | import setuptools 17 | 18 | setuptools.setup( 19 | setup_requires=['pbr>=2.0.0'], 20 | pbr=True) 21 | -------------------------------------------------------------------------------- /test-requirements.txt: -------------------------------------------------------------------------------- 1 | fixtures>=3.0.0 # Apache-2.0/BSD 2 | stestr>=2.0.0 # Apache-2.0 3 | testscenarios>=0.4 # Apache-2.0/BSD 4 | testtools>=2.2.0 # MIT 5 | oslotest>=3.2.0 # Apache-2.0 6 | pifpaf>=2.2.0 # Apache-2.0 7 | 8 | # for test_impl_kafka 9 | confluent-kafka>=1.3.0 # Apache-2.0 10 | 11 | coverage>=4.0 # Apache-2.0 12 | 13 | eventlet>=0.23.0 # MIT 14 | greenlet>=0.4.15 # MIT 15 | -------------------------------------------------------------------------------- /tools/functions.sh: -------------------------------------------------------------------------------- 1 | 2 | wait_for_line () { 3 | while read line 4 | do 5 | echo "$line" | grep -q "$1" && break 6 | echo "$line" | grep "$2" && exit 1 7 | done < "$3" 8 | # Read the fifo for ever otherwise process would block 9 | cat "$3" >/dev/null & 10 | } 11 | 12 | function clean_exit(){ 13 | local error_code="$?" 14 | for job in `jobs -p` 15 | do 16 | kill -9 $job 17 | done 18 | rm -rf "$1" 19 | return $error_code 20 | } 21 | 22 | 23 | -------------------------------------------------------------------------------- /tools/setup-scenario-env.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | set -e 3 | 4 | . tools/functions.sh 5 | 6 | SCENARIO=${SCENARIO:-"scenario01"} 7 | 8 | function _setup_kafka { 9 | 10 | SCALA_VERSION=${SCALA_VERSION:-"2.12"} 11 | KAFKA_VERSION=${KAFKA_VERSION:-"2.0.0"} 12 | 13 | if [[ -z "$(which kafka-server-start)" ]] && [[ -z $(which kafka-server-start.sh) ]]; then 14 | DATADIR=$(mktemp -d /tmp/OSLOMSG-KAFKA.XXXXX) 15 | trap "clean_exit $DATADIR" EXIT 16 | 17 | tarball=kafka_${SCALA_VERSION}-${KAFKA_VERSION}.tgz 18 | 19 | wget http://archive.apache.org/dist/kafka/${KAFKA_VERSION}/$tarball -O $DATADIR/$tarball 20 | tar -xzf $DATADIR/$tarball -C $DATADIR 21 | export PATH=$DATADIR/kafka_${SCALA_VERSION}-${KAFKA_VERSION}/bin:$PATH 22 | fi 23 | } 24 | 25 | case $SCENARIO in 26 | scenario01) 27 | export RPC_TRANSPORT_URL=rabbit://pifpaf:secret@127.0.0.1:5682/ 28 | export NOTIFY_TRANSPORT_URL=rabbit://pifpaf:secret@127.0.0.1:5682/ 29 | RUN="--env-prefix RABBITMQ run rabbitmq" 30 | ;; 31 | scenario02) 32 | _setup_kafka 33 | export RPC_TRANSPORT_URL=rabbit://pifpaf:secret@127.0.0.1:5682/ 34 | export NOTIFY_TRANSPORT_URL=kafka://127.0.0.1:9092/ 35 | RUN="--env-prefix RABBITMQ run rabbitmq -- pifpaf --env-prefix KAFKA run kafka" 36 | ;; 37 | *) ;; 38 | esac 39 | 40 | pifpaf $RUN -- $* 41 | -------------------------------------------------------------------------------- /tox.ini: -------------------------------------------------------------------------------- 1 | [tox] 2 | minversion = 3.18.0 3 | envlist = py3, pep8 4 | 5 | [testenv] 6 | passenv = 7 | OS_* 8 | ZUUL_CACHE_DIR 9 | REQUIREMENTS_PIP_LOCATION 10 | deps = 11 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 12 | -r{toxinidir}/test-requirements.txt 13 | -r{toxinidir}/requirements.txt 14 | commands = 15 | stestr run --slowest {posargs} 16 | 17 | [testenv:pep8] 18 | skip_install = true 19 | deps = 20 | pre-commit>=2.6.0 # MIT 21 | commands = 22 | pre-commit run -a 23 | 24 | [testenv:cover] 25 | setenv = 26 | PYTHON=coverage run --source oslo_messaging --parallel-mode 27 | commands = 28 | coverage erase 29 | stestr run --slowest {posargs} 30 | coverage combine 31 | coverage html -d cover 32 | coverage report 33 | coverage report --show-missing 34 | 35 | [testenv:venv] 36 | commands = {posargs} 37 | 38 | [testenv:docs] 39 | allowlist_externals = rm 40 | deps = 41 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 42 | -r{toxinidir}/doc/requirements.txt 43 | commands = 44 | rm -fr doc/build 45 | sphinx-build -W --keep-going -b html doc/source doc/build/html 46 | 47 | # The following functional test scenarios are defined for the 48 | # testing of the messaging backends and to demonstrated the functional 49 | # correctness across driver combinations (e.g. RPC and Notify) 50 | # 51 | # RPC Notify 52 | # -------- -------- 53 | # scenario01 rabbit rabbit 54 | # scenario02 rabbit kafka 55 | # 56 | [testenv:py310-func-scenario01] 57 | setenv = 58 | SCENARIO=scenario01 59 | allowlist_externals = 60 | {toxinidir}/tools/setup-scenario-env.sh 61 | commands = 62 | {toxinidir}/tools/setup-scenario-env.sh stestr run --slowest {posargs:oslo_messaging.tests.functional} 63 | 64 | [testenv:py310-func-scenario02] 65 | setenv = 66 | SCENARIO=scenario02 67 | allowlist_externals = 68 | {toxinidir}/tools/setup-scenario-env.sh 69 | commands = 70 | {toxinidir}/tools/setup-scenario-env.sh stestr run --slowest {posargs:oslo_messaging.tests.functional} 71 | 72 | [flake8] 73 | show-source = True 74 | enable-extensions = H203,H106 75 | # E731 skipped as assign a lambda expression 76 | ignore = E731,H405,W504 77 | exclude = .tox,dist,doc,*.egg,build,__init__.py 78 | 79 | [hacking] 80 | import_exceptions = 81 | 82 | [flake8:local-plugins] 83 | extension = 84 | O321 = checks:check_oslo_namespace_imports 85 | O324 = checks:CheckForLoggingIssues 86 | paths = ./oslo_messaging/hacking 87 | 88 | [testenv:releasenotes] 89 | allowlist_externals = 90 | rm 91 | commands = 92 | rm -rf releasenotes/build 93 | sphinx-build -a -E -W -d releasenotes/build/doctrees --keep-going -b html releasenotes/source releasenotes/build/html 94 | deps = 95 | -c{env:TOX_CONSTRAINTS_FILE:https://releases.openstack.org/constraints/upper/master} 96 | -r{toxinidir}/doc/requirements.txt 97 | 98 | [testenv:bindep] 99 | deps = 100 | bindep 101 | commands = bindep {posargs} 102 | --------------------------------------------------------------------------------