{"id":20111513,"url":"https://github.com/openstack/charms.openstack","last_synced_at":"2025-05-06T11:30:45.532Z","repository":{"id":45074428,"uuid":"62124382","full_name":"openstack/charms.openstack","owner":"openstack","description":"Helpers for writing layered+reactive OpenStack Charms. Mirror of code maintained at opendev.org.","archived":false,"fork":false,"pushed_at":"2024-05-28T11:27:56.000Z","size":793,"stargazers_count":14,"open_issues_count":0,"forks_count":15,"subscribers_count":6,"default_branch":"master","last_synced_at":"2024-05-29T02:59:21.893Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://opendev.org/openstack/charms.openstack","language":"Python","has_issues":false,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/openstack.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.rst","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-06-28T08:38:14.000Z","updated_at":"2024-05-30T06:19:31.474Z","dependencies_parsed_at":"2024-05-28T14:13:09.438Z","dependency_job_id":null,"html_url":"https://github.com/openstack/charms.openstack","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openstack%2Fcharms.openstack","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openstack%2Fcharms.openstack/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openstack%2Fcharms.openstack/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/openstack%2Fcharms.openstack/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/openstack","download_url":"https://codeload.github.com/openstack/charms.openstack/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":224499851,"owners_count":17321605,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-13T18:16:21.884Z","updated_at":"2024-11-13T18:16:22.392Z","avatar_url":"https://github.com/openstack.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Team and repository tags\n========================\n\n[![Team and repository tags](http://governance.openstack.org/badges/charms.openstack.svg)](http://governance.openstack.org/reference/tags/index.html)\n\n\u003c!-- Change things from this point on --\u003e\n\n# charms.openstack\n\nHelpers for building layered, reactive OpenStack charms.\n\n# Support and discussions\n\nWe use the openstack-charmers mailing-lists for developer and user discussions, you can\nfind and subscribe here: https://lists.ubuntu.com/openstack-charmers.\n\nIf you prefer live discussions, some of us also hang out in\n[#juju](https://chat.charmhub.io/charmhub/channels/juju) on Charmhub chat.\n\n# Bug reports\n\nBug reports can be filed at https://bugs.launchpad.net/charms.openstack/+filebug\n\n# Using `charms.openstack`\n\ncharms.openstack provides a module `charms_openstack` which is included in\nlayer-openstack's `wheelhouse.txt`. It is provides the fundamental\nfunctionality required of _most_ OpenStack charms.\n\nThe main classes that the module provides are:\n\n * :class:`OpenStackRelationAdapter`\n * :class:`RabbitMQRelationAdapter`\n * :class:`DatabaseRelationAdapter`\n * :class:`ConfigurationAdapter`\n * :class:`OpenStackRelationsAdapter`\n * :class:`OpenStackCharm`\n\n# Key features of `charms.openstack`\n\nThe main features that `charms.openstack` provides are:\n\n * a base `OpenStackCharm` that provides:\n   * The ability to specify the OpenStack release that the charm works with.\n   * The list of packages to install on the charm.\n   * The ports that the charm exposes.\n   * The keystone service type (if applicable)\n   * A mapping of config files to services to restart if the configuration\n     changes.\n   * The required relations for the charm (workload status)\n   * The sync command that the database (if associated) will need for its\n     schema.\n   * a default install that gets the packages, installs them, and sets the\n     appropriate workload status.\n   * A configuration file renderer (using the relation adapters) to write\n     the configuration files for the service being managed.\n   * A workload status helper (`assess_status()`) that checks the state of\n     interfaces, the services, and ports, and sets the workload status. This\n     is automatically provided for the `update-status` hook in the `layer-openstack`\n     layer.\n\n# How to leverage `charms.openstack` classes\n\n## Using `OpenStackCharm`\n\n`OpenStackCharm()` and the related classes provide a powerful framework to\nbuild an OpenStack charm on.  There are two approaches to writing charms that\nsupport multiple OpenStack releases.  Note that determining the release _is up\nto the charm author_, and can be signalled to `OpenStackCharm` in two ways.\n\n 1. Write a single `OpenStackCharm` derived class that uses `self.release` to\n    determine what functionality to exhibit depending on the release. In this\n    case, there is no need to register multiple charms and provide a _chooser_\n    to determine which class to use.\n\n 2. Write multiple `OpenStackCharm` derived classes which map to each difference\n    in charm functionality depending on the release, and register a _chooser_\n    function using the `@register_os_release_selector` decorator.\n\ne.g.\n\n```python\nclass LibertyCharm(OpenStackCharm):\n    release = 'liberty'\n\nclass MitakaCharm(OpenStackCharm):\n    release = 'mitaka'\n\n@register_os_release_selector\ndef choose_release():\n    \"\"\"Determine the release based on the python-keystonemiddleware that is\n    installed.\n    \"\"\"\n    return ch_utils.os_release('python-keystonemiddleware')\n```\n\nThis will automatically select `LibertyCharm` for a liberty release and\n`MitakaCharm` for the mitaka release.  Note, that it will also _set_ `release`\non the `OpenStackCharm` instance via the `__init__()` method, so that the\ninstance knows what the charm is.\n\nIf only a single charm class is needed, the `__init__()` method of the\nclass can be used to determine the release instead:\n\n```python\nclass TheCharm(OpenStackCharm):\n    release = 'liberty'\n\n    def __init__(release=None, *args, **kwargs):\n        if release is None:\n            release = ch_utils.os_release('python-keystonemiddleware')\n        super(TheCharm, self).__init__(release=release, *args, **kwargs)\n```\n\nIf the release selector function is registered, then the overridden\n`__init__()` method is not needed as the release will be passed into the\ndefault `__init__()` method.  However, there may be other functionality that\nthe charm author needs to include in the initialiser.\n\nNote that using `os_release()` can typically be used to determine the release\nof OpenStack.\n\n## Using the relation adapter classes - OpenStackRelationAdapter\n\nThe relation adapter classes adapt a reactive interface for use in the\nrendering functions.  Their principal use is to provide an iterator of the\nattributes declared in the `assessors` attribute of the instance.\n\nA reactive `BaseRelation` derived instance has an `auto_accessors` attribute\nwhich declares the variables that the relation has.  These are copied into the\n`accessors` attribute of the `OpenStackRelationAdapter` class, and additional\nattributes can be added as part of class instantiation.\n\nNote that the `accessor` properties are _dynamic_, in that they call the\nunderlying relation property when they are accessed.\n\nThe _purpose_ of the `OpenStackRelation` class is for the instance to be used\nas part of configuration file rendering, as an instance of an\n`OpenStackRelation` class can be passed to the render function, and the\niterator will provide the _key value_ pairs to the template processor.\n\nA derived `OpenStackRelation` class can provide additional _computed_\nproperties as required. e.g. the `RabbitMQRelationAdapter` implementation:\n\n```python\nclass RabbitMQRelationAdapter(OpenStackRelationAdapter):\n    \"\"\"\n    Adapter for the RabbitMQRequires relation interface.\n    \"\"\"\n\n    interface_type = \"messaging\"\n\n    def __init__(self, relation):\n        add_accessors = ['vhost', 'username']\n        super(RabbitMQRelationAdapter, self).__init__(relation, add_accessors)\n\n    @property\n    def host(self):\n        \"\"\"\n        Hostname that should be used to access RabbitMQ.\n        \"\"\"\n        if self.vip:\n            return self.vip\n        else:\n            return self.private_address\n\n    @property\n    def hosts(self):\n        \"\"\"\n        Comma separated list of hosts that should be used\n        to access RabbitMQ.\n        \"\"\"\n        hosts = self.relation.rabbitmq_hosts()\n        if len(hosts) \u003e 1:\n            return ','.join(hosts)\n        else:\n            return None\n```\n\nNote that the additional accessors `vhost` and `username` are provided in the\noverridden `__init__()` method.\n\n## The `ConfigurationAdapter`\n\nThe `ConfigurationAdapter` class simply provides _snapshot_ of the\nconfiguration options for the current charm, such that they can be accessed\nas attributes of an instance of the class.  e.g. rather than `config('vip')`\nthen user can use `c_adapter.vip`.\n\nThe benefit, is that a _derived_ version of `ConfigurationAdapter` can be\nprovided that has _computed_ properties that can be used like static properties\non the instance.  The `ConfigurationAdapter`, or derived class, is used with\nthe `OpenStackRelationAdapters` class (not the plural _...Adapters_) class that\nbrings together all of the relations into one place.\n\n## The `OpenStackRelationAdapters` class\n\nThe `OpenStackRelationAdapters` class joins together the relation adapter\nclasses, with the `ConfigurationAdapter` (or derived) class, and works _like_\na charmhelpers `OSRenderConfig` instance to the rendering functions in\ncharmhelpers.\n\nThus an instance of the `OpenStackRelationAdapters` (or derived) class is used\nin  the `charmhelpers.core.templating.render()` function to provide the\nvariables needed to render templates.\n\nThe `OpenStackRelationAdapters` class can be subclassed (derived) with\nadditional custom `OpenStackRelationAdapter` classes (to map to particular\nrelations) using the `relation_adapters` class property:\n\n```python\nclass MyRelationAdapters(OpenStackRelationAdapters):\n\n    relation_adapters = {\n       'my-relation': MyRelationAdapter,\n    }\n```\n\nThis enables custom relation adapters to be mapped to particular relations\nsuch that custom functionality can be implemented for a particular reactive\nrelationship.\n\n## HighAvailability Support\n\nTo be completed.\n\n## OpenStack Upgrade via config\n\nAn OpenStack principle charm has an 'openstack-origin' configuration option.\nThis is used to setup the package source for a charm. If a user updates this\noption to point at a package repository then the charm can be configured to\nautomatically upgrade. This is achieved with the following steps:\n\n 1. Add hook to reactive handler\n\n```python\n@reactive.when(*COMPLETE_INTERFACE_STATES)\ndef config_changed(args):\n    MyCharm.singleton.upgrade_if_available(args)\n```\n 2. Define the package for the charm to monitor and a mapping of OpenStack\n    releases to package versions.\n\n```python\nclass TheCharm(OpenStackCharm):\n\n    release_pkg = 'pkg-name'\n    package_codenames = {\n        'pkg-name': collections.OrderedDict([\n            ('2', 'mitaka'),\n            ('3', 'newton'),\n            ('4', 'ocata'),\n        ]),\n    }\n```\n\n## Workload status\n\nOpenStack charms support the concept of _workload status_ which helps to inform\na user of the charm of the current state of the charm.  The following workload\nstatuses are supported:\n\n * unknown - The charm _doesn't_ support workload status.  This should **not**\n   be used for charms that DO support workload status.\n * active - The unit under the charms control is fully configuration\n   and available for use.\n * maintenance - the unit is installing, or doing something of that nature.\n * waiting - The unit is waiting for a relation to become available. i.e. the\n   relation is not yet _complete_ in that some data is missing still.\n * blocked - a relation is not yet connected, or some other blocking\n   condition.\n * paused - (Not yet available) - the unit has been put into the paused state.\n\nThe default is for charms to support workload status, and the default\ninstallation method sets the status to maintenance with an install message.\n\nIf the charm is not going to support workload status, _and this is not\nrecommended_, then the charm author will need to override the `install()`\nmethod of `OpenStackCharm` derived class to disable setting the `maintenance`\nstate, and override the `assess_status()` method to a NOP.\n\nThe `assess_status()` method on `OpenStackCharm` provides a helper to enable\nthe charm author to provide workload status.  By default:\n\n * The actual assessment of status is deferred until the all of the reactive\n   handlers have had a chance to execute (according to their conditions), just\n   before the charm hook exits.  The real `assess_status()` method is actually\n   `_assess_status()` and the `assess_status()` method simply sets up an\n   `atexit()` hook to defer the operation.  This means that you can call\n   `assess_status()` multiple times BUT it will actually only be invoked at the\n   end of the charm hook execution.  If you _need_ to actually run\n   assess_status() at the point in the handler, then call `_assess_status()`.\n * The install method provides the maintenance status.\n * The `layer-openstack` layer provides a hook for `update-status` which\n   calls the `assess_status()` function on the charm class.\n * The `_assess_status()` method uses various attributes of the class to provide\n   a default mechanism for assessing the workload status of the charm/unit.\n\nThe latter is extremely useful for determining the workload status. The\n`_assess_status()` method does the following checks:\n\n 1. The unit checks if it is paused. (Not yet available as a feature).\n 2. The unit checks the relations to see if they are connected and available.\n 3. The unit checks `custom_assess_status_check()`\n 4. The unit checks that the services are running and ports are open.\n\n### Checking of relations\n\nThe assess_status function checks that the relations named in the class\nattribute `required_relations` are connected and available.  It does this using\nthe convention of:\n\n * A connected relation has the `{relation}.connected` state set.\n * An available relation has the `{relation}.available` state set.\n\nThis is a convention that the interfaces (e.g. interface-keystone, etc.) use.\ninterface-keystone sets `identity-service.connected` when it has a connection\nwith keystone, and `identity-service.available` when the connection is\ncompleted and all information transferred.\n\nThat if `required_relations` is `['identity-service']`, then the\n`assess_status()` function will check for `identity-service.connected` and\n`identity-service.available` states.\n\nIf the charm author requires additional states to be checked for an interface,\nthen the  method `states_to_check` should be overridden in the derived class and\nadditional states, the status and error message provided.  See the code for\nfurther details.\n\ne.g.\n\n```python\ndef states_to_check():\n    states = super(MyCharm, self).states_to_check()\n    states['some-relation'].append(\n        (\"some-relation.available.ssl\", \"waiting\", \"'some-relation' incomplete\"))\n    return states\n```\n\n### The `custom_assess_status_check()` method\n\nIf the charm author needs to do additional status checking, then the\n`custom_assess_status_check()` method should be overridden in the derived\nclass. The return value from the method is:\n\n * (None, None) - the unit is fine.\n * status, message - the unit's workload status is not active.\n\n### Not checking services are running\n\nBy default, the `_assess_status()` method checks that the services declared in\nthe class attribute `services` (list of strings) are checked to ensure that\nthey are running.  Additionally, the ports declared in the class attribute\n`api_ports` are also checked for being _listened on_.\n\nHowever, if the services check is not required, then the derived class should\noverload the `check_running_services()` method and return `None, None`.\n\nAdditionally, if the services running check _is_ required, but the ports should\nnot be checked, then the `ports_to_check` method can be overridden and return\nan empty list `[]`.\n\n### Using `assess_status()`\n\nThe `assess_status()` method should be used on any hook or state method where\nthe unit's status may have changed.  e.g. interfaces connecting or becoming\navailable, configuration changes, etc.\n\ne.g.\n\n```python\n@reactive.when('amqp.connected')\ndef setup_amqp_req(amqp):¬\n    \"\"\"Use the amqp interface to request access to the amqp broker using our\n    local configuration.\n    \"\"\"\n    amqp.request_access(username=hookenv.config('rabbit-user'),\n                        vhost=hookenv.config('rabbit-vhost'))\n    MyCharm.singleton.assess_status()\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenstack%2Fcharms.openstack","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fopenstack%2Fcharms.openstack","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fopenstack%2Fcharms.openstack/lists"}