Charm Helpers Documentation¶
The charmhelpers
Python library is an extensive collection of functions and classes
for simplifying the development of Juju Charms. It includes utilities for:
- Interacting with the host environment
- Managing hook events
- Reading and writing charm configuration
- Installing dependencies
- Much, much more!
Getting Started¶
For a video introduction to charmhelpers
, check out this
Charm School session. To start
using charmhelpers
, proceed with the instructions on the remainder of this
page.
Installing Charm Tools¶
First, follow these instructions
to install the charm-tools
package for your platform.
Creating a New Charm¶
$ cd ~
$ mkdirs -p charms/precise
$ cd charms/precise
$ charm create -t python mycharm
INFO: Generating template for mycharm in ./mycharm
INFO: No mycharm in apt cache; creating an empty charm instead.
Symlink all hooks to one python source file? [yN] y
INFO:root:Loading charm helper config from charm-helpers.yaml.
INFO:root:Checking out lp:charm-helpers to /tmp/tmpPAqUyN/charm-helpers.
Branched 160 revisions.
INFO:root:Syncing directory: /tmp/tmpPAqUyN/charm-helpers/charmhelpers/core -> lib/charmhelpers/core.
INFO:root:Adding missing __init__.py: lib/charmhelpers/__init__.py
Let’s see what our new charm looks like:
$ tree mycharm/
mycharm/
├── charm-helpers.yaml
├── config.yaml
├── hooks
│ ├── config-changed -> hooks.py
│ ├── hooks.py
│ ├── install -> hooks.py
│ ├── start -> hooks.py
│ ├── stop -> hooks.py
│ └── upgrade-charm -> hooks.py
├── icon.svg
├── lib
│ └── charmhelpers
│ ├── core
│ │ ├── fstab.py
│ │ ├── hookenv.py
│ │ ├── host.py
│ │ └── __init__.py
│ └── __init__.py
├── metadata.yaml
├── README.ex
├── revision
├── scripts
│ └── charm_helpers_sync.py
└── tests
├── 00-setup
└── 10-deploy
6 directories, 20 files
The charmhelpers
code is bundled in our charm in the lib/
directory.
All of our python code will go in hooks/hook.py
. A look at that file reveals
that charmhelpers
has been added to the python path and imported for us:
$ head mycharm/hooks/hooks.py -n11
#!/usr/bin/python
import os
import sys
sys.path.insert(0, os.path.join(os.environ['CHARM_DIR'], 'lib'))
from charmhelpers.core import (
hookenv,
host,
)
Updating Charmhelpers Packages¶
By default, a new charm installs only the charmhelpers.core
package, but
other packages are available (for a complete list, see the API Documentation).
The installed packages are controlled by the charm-helpers.yaml
file in our charm:
$ cd mycharm
$ cat charm-helpers.yaml
destination: lib/charmhelpers
branch: lp:charm-helpers
include:
- core
Let’s update this file to include some more packages:
$ vim charm-helpers.yaml
$ cat charm-helpers.yaml
destination: lib/charmhelpers
branch: lp:charm-helpers
include:
- core
- contrib.storage
- fetch
Now we need to download the new packages into our charm:
$ ./scripts/charm_helpers_sync.py -c charm-helpers.yaml
INFO:root:Loading charm helper config from charm-helpers.yaml.
INFO:root:Checking out lp:charm-helpers to /tmp/tmpT38Y87/charm-helpers.
Branched 160 revisions.
INFO:root:Syncing directory: /tmp/tmpT38Y87/charm-helpers/charmhelpers/core -> lib/charmhelpers/core.
INFO:root:Syncing directory: /tmp/tmpT38Y87/charm-helpers/charmhelpers/contrib/storage -> lib/charmhelpers/contrib/storage.
INFO:root:Adding missing __init__.py: lib/charmhelpers/contrib/__init__.py
INFO:root:Syncing directory: /tmp/tmpT38Y87/charm-helpers/charmhelpers/fetch -> lib/charmhelpers/fetch.
A look at our charmhelpers directory reveals that the new packages have indeed been added. We are now free to import and use them in our charm:
$ tree lib/charmhelpers/
lib/charmhelpers/
├── contrib
│ ├── __init__.py
│ └── storage
│ ├── __init__.py
│ └── linux
│ ├── ceph.py
│ ├── __init__.py
│ ├── loopback.py
│ ├── lvm.py
│ └── utils.py
├── core
│ ├── fstab.py
│ ├── hookenv.py
│ ├── host.py
│ └── __init__.py
├── fetch
│ ├── archiveurl.py
│ ├── bzrurl.py
│ └── __init__.py
└── __init__.py
5 directories, 15 files
Next Steps¶
Now that you have access to charmhelpers
in your charm, check out the
Examples or API Documentation to learn about all the great
functionality that charmhelpers
provides.
Examples¶
If you’d like to contribute an example (please do!), please refer to the Contributing page for instructions on how to do so.
Interacting with Charm Configuration¶
The charmhelpers.core.hookenv.config()
, when called with no arguments,
returns a charmhelpers.core.hookenv.Config
instance - a dictionary
representation of a charm’s config.yaml
file. This object can
be used to:
- get a charm’s current config values
- check if a config value has changed since the last hook invocation
- view the previous value of a changed config item
- save arbitrary key/value data for use in a later hook
For the following examples we’ll assume our charm has a config.yaml file that looks like this:
options:
app-name:
type: string
default: "My App"
description: "Name of your app."
Getting charm config values¶
# hooks/hooks.py
from charmhelpers.core import hookenv
hooks = hookenv.Hooks()
@hooks.hook('install')
def install():
config = hookenv.config()
assert config['app-name'] == 'My App'
Checking if a config value has changed¶
Let’s say the user changes the app-name
config value at runtime by
executing the following juju command:
juju set mycharm app-name="My New App"
which triggers a config-changed
hook:
# hooks/hooks.py
from charmhelpers.core import hookenv
hooks = hookenv.Hooks()
@hooks.hook('config-changed')
def config_changed():
config = hookenv.config()
assert config.changed('app-name')
assert config['app-name'] == 'My New App'
assert config.previous('app-name') == 'My App'
Saving arbitrary key/value data¶
The Config
object maybe also be
used to store arbitrary data that you want to persist across hook
invocations:
# hooks/hooks.py
from charmhelpers.core import hookenv
hooks = hookenv.Hooks()
@hooks.hook('install')
def install():
config = hookenv.config()
config['mykey'] = 'myval'
@hooks.hook('config-changed')
def config_changed():
config = hookenv.config()
assert config['mykey'] == 'myval'
Managing Charms with the Services Framework [DEPRECATED]¶
Traditional charm authoring is focused on implementing hooks. That is, the charm author is thinking in terms of “What hook am I handling; what does this hook need to do?” However, in most cases, the real question should be “Do I have the information I need to configure and start this piece of software and, if so, what are the steps for doing so?” The services framework tries to bring the focus to the data and the setup tasks, in the most declarative way possible.
Hooks as Data Sources for Service Definitions¶
While the install
, start
, and stop
hooks clearly represent
state transitions, all of the other hooks are really notifications of
changes in data from external sources, such as config data values in
the case of config-changed
or relation data for any of the
*-relation-*
hooks. Moreover, many charms that rely on external
data from config options or relations find themselves needing some
piece of external data before they can even configure and start anything,
and so the start
hook loses its semantic usefulness.
If data is required from multiple sources, it even becomes impossible to know which hook will be executing when all required data is available. (E.g., which relation will be the last to execute; will the required config option be set before or after all of the relations are available?) One common solution to this problem is to create “flag files” to track whether a given bit of data has been observed, but this can get cluttered quickly and is difficult to understand what conditions lead to which actions.
When using the services framework, all hooks other than install
are handled by a single call to manager.manage()
.
This can be done with symlinks, or by having a definitions.py
file
containing the service defintions, and every hook can be reduced to:
#!/bin/env python
from charmhelpers.core.services import ServiceManager
from definitions import service_definitions
ServiceManager(service_definitions).manage()
So, what magic goes into definitions.py
?
Service Definitions Overview¶
The format of service definitions are fully documented in
ServiceManager
, but most commonly
will consist of one or more dictionaries containing four items: the name of
a service being managed, the list of data contexts required before the service
can be configured and started, the list of actions to take when the data
requirements are satisfied, and list of ports to open. The service name
generally maps to an Upstart job, the required data contexts are dict
or dict
-like structures that contain the data once available (usually
subclasses of RelationContext
or wrappers around charmhelpers.core.hookenv.config()
), and the actions
are just callbacks that are passed the service name for which they are executing
(or a subclass of ManagerCallback
for more complex cases).
An example service definition might be:
service_definitions = [
{
'service': 'wordpress',
'ports': [80],
'required_data': [config(), MySQLRelation()],
'data_ready': [
actions.install_frontend,
services.render_template(source='wp-config.php.j2',
target=os.path.join(WP_INSTALL_DIR, 'wp-config.php'))
services.render_template(source='wordpress.upstart.j2',
target='/etc/init/wordpress'),
],
},
]
Each time a hook is fired, the conditions will be checked (in this case, just that MySQL is available) and, if met, the appropriate actions taken (correct front-end installed, config files written / updated, and the Upstart job (re)started, implicitly).
Required Data Contexts¶
Required data contexts are, at the most basic level, are just dictionaries,
and if they evaluate as True (e.g., if the contain data), their condition is
considered to be met. A simple sentinal could just be a function that returns
data if available or an empty dict
otherwise.
For the common case of gathering data from relations, the
RelationContext
base class gathers
data from a named relation and checks for a set of required keys to be present
and set on the relation before considering that relation complete. For example,
a basic MySQL context might be:
class MySQLRelation(RelationContext):
name = 'db'
interface = 'mysql'
required_keys = ['host', 'user', 'password', 'database']
Because there could potentially be multiple units on a given relation, and
to prevent conflicts when the data contexts are merged to be sent to templates
(see below), the data for a RelationContext
is nested in the following way:
relation[relation.name][unit_number][relation_key]
For example, to get the host of the first MySQL unit (mysql/0
):
mysql = MySQLRelation()
unit_0_host = mysql[mysql.name][0]['host']
Note that only units that have set values for all of the required keys are
included in the list, and if no units have set all of the required keys,
instantiating the RelationContext
will result in an empty list.
Data-Ready Actions¶
When a hook is triggered and all of the required_data
contexts are complete,
the list of “data ready” actions are executed. These callbacks are passed
the service name from the service
key of the service definition for which
they are running, and are responsible for (re)configuring the service
according to the required data.
The most common action should be to render a config file from a template.
The render_template
helper will merge all of the required_data
contexts and render a
Jinja2 template with the combined data. For
example, to render a list of DSNs for units on the db relation, the
template should include:
databases: [
{% for unit in db %}
"mysql://{{unit['user']}}:{{unit['password']}}@{{unit['host']}}/{{unit['database']}}",
{% endfor %}
]
Note that the actions need to be idempotent, since they will all be re-run if something about the charm changes (that is, if a hook is triggered). That is why rendering a template is preferred to editing a file via regular expression substitutions.
Also note that the actions are not responsible for starting the service; there
are separate start
and stop
options that default to starting and stopping
an Upstart service with the name given by the service
value.
Conclusion¶
By using this framework, it is easy to see what the preconditions for the charm are, and there is never a concern about things being in a partially configured state. As a charm author, you can focus on what is important to you: what data is mandatory, what is optional, and what actions should be taken once the requirements are met.
Contributing¶
All contributions, both code and documentation, are welcome!
Source¶
The source code is located at https://code.launchpad.net/charm-helpers. To submit contributions you’ll need to create a Launchpad account if you do not already have one.
To get the code:
$ bzr branch lp:charm-helpers
To build and run tests:
$ cd charm-helpers
$ make
Submitting a Merge Proposal¶
Run make test
and ensure all tests pass. Then commit your changes and push
them to a personal branch:
bzr ci -m "Description of your changes"
bzr push lp:~<launchpad-username>/charm-helpers/my-feature
Note that the branch name (‘my-feature’ in the above example) can be anything you choose - preferably something descriptive.
Once your branch is pushed, open it in a web browser, e.g.:
https://code.launchpad.net/~<launchpad-username>/charm-helpers/my-feature
Find and click on the ‘Propose for merging’ link, and on the following screen, click the ‘Propose Merge’ button.
Note
Do not set a value in the ‘Reviewer’ field - it will be set automatically.
Open Bugs¶
If you’re looking for something to work on, the open bug/feature list can be found at https://bugs.launchpad.net/charm-helpers.
Documentation¶
If you’d like to contribute to the documentation, please refer to the HACKING
document in the root of the source tree for instructions on building the documentation.
Contributions to the Examples section of the documentation are
especially welcome, and are easy to add. Simply add a new .rst
file under
charmhelpers/docs/examples
.
Getting Help¶
If you need help you can find it in #juju
on the Freenode IRC network. Come
talk to us - we’re a friendly bunch!
API Documentation¶
charmhelpers.core package¶
charmhelpers.core.reactive¶
This module serves as the basis for creating charms and relation implementations using the reactive pattern.
Overview¶
The pattern is “reactive” because you use @when
and similar decorators to indicate that blocks of code react to certain conditions,
such as a relation reaching a specific state, a file changing, certain config
values being set, etc. More importantly, you can react to not just indvidual
conditions, but meaningful combinations of conditions that can span multiple hook
invocations, in a natural way.
For example, the following would update a config file when both a database and admin password were available, and, if and only if that file was changed, the appropriate service would be restarted:
@when('db.database.available', 'admin-pass')
def render_config(pgsql):
render_template('app-config.j2', '/etc/app.conf', {
'db_conn': pgsql.connection_string(),
'admin_pass': hookenv.config('admin-pass'),
})
@when_file_changed('/etc/app.conf')
def restart_service():
hookenv.service_restart('myapp')
if __name__ == '__main__':
reactive.main()
Structure of a Reactive Charm¶
The structure of a reactive charm is similar to existing charms, with the
addition of reactive
and relations
directories under hooks
:
.
├── metadata.yaml
└── hooks
├── pgsql-relation-changed
├── reactive
│ └── common.py
└── relations
└── pgsql
├── common
│ └── __init__.py
├── interface.yaml
├── peer.py
├── provides.py
└── requires.py
The hooks will need to call reactive.main()
,
and the decorated handler blocks can be placed in any file under the reactive
directory. The relations
directory can contain any relation stub implementations
that your charm uses.
If using Charm Composition, as is recommended, the relation hooks and relations
directories will be automatically managed for you based on your metadata.yaml
,
so you can focus on writing just the install
and config-changed
hooks, and
the reactive
handler files.
Relation Stubs¶
A big part of the reactive pattern is the use of relation stubs. These are
classes, based on RelationBase
,
that are reponsible for managing the conversation with remote services or units
and informing the charm when the conversation has reached key points, called
states, upon which the charm can act and do useful work. They allow a single
interface author to create code to handle both sides of the conversation, and
to expose a well-defined API to charm authors.
Relation stubs allows charm authors to focus on implementing the behavior and resources that the relation provides, while the interface author focuses on the communication necessary to get that behavior and resources between the related services. In general, the author of the charm that provides a particular interface is likely to be the interface author that creates both the provides and requires sides of the relation. After that, charm authors that wish to make use of that interface can just re-use the existing relation stub.
Non-Python Reactive Handlers¶
Reactive handlers can be written in any language, provided they conform to
the ExternalHandler
protocol. In short, they
must accept a --test
and --invoke
argument and do the appropriate
thing when called with each.
There are helpers for writing handlers in bash. For example:
source `which reactive.sh`
@when 'db.database.available' 'admin-pass'
function render_config() {
db_conn=$(state_relation_call 'db.database.available' connection_string)
admin_pass=$(config-get 'admin-pass')
chlp render_template 'app-config.j2' '/etc/app.conf' --db_conn="$db_conn" --admin_pass="$admin_pass"
}
@when_not 'db.database.available'
function no_db() {
status-set waiting 'Waiting on database'
}
@when_not 'admin-pass'
function no_db() {
status-set blocked 'Missing admin password'
}
@when_file_changed '/etc/app.conf'
function restart_service() {
service myapp restart
}
reactive_handler_main
Reactive API Documentation¶
charmhelpers.core.reactive.decorators¶
hook |
Register the decorated function to run when the current hook matches any of the hook_patterns . |
not_unless |
Assert that the decorated function can only be called if the desired_states are active. |
only_once |
Ensure that the decorated function is only executed the first time it is called. |
when |
Register the decorated function to run when all desired_states are active. |
when_file_changed |
Register the decorated function to run when one or more files have changed. |
when_not |
Register the decorated function to run when not all desired_states are active. |
-
charmhelpers.core.reactive.decorators.
hook
(*hook_patterns)¶ Register the decorated function to run when the current hook matches any of the
hook_patterns
.The hook patterns can use the
{interface:...}
and{A,B,...}
syntax supported byany_hook()
.If the hook is a relation hook, an instance of that relation class will be passed in to the decorated function.
For example, to match any joined or changed hook for the relation providing the
mysql
interface:class MySQLRelation(RelationBase): @hook('{provides:mysql}-relation-{joined,changed}') def joined_or_changed(self): pass
-
charmhelpers.core.reactive.decorators.
not_unless
(*desired_states)¶ Assert that the decorated function can only be called if the desired_states are active.
Note that, unlike
when()
, this does not trigger the decorated function if the states match. It only raises an exception if the function is called when the states do not match.This is primarily for informational purposes and as a guard clause.
-
charmhelpers.core.reactive.decorators.
only_once
(action)¶ Ensure that the decorated function is only executed the first time it is called.
This can be used on reactive handlers to ensure that they are only triggered once, even if their conditions continue to match on subsequent calls, even across hook invocations.
-
charmhelpers.core.reactive.decorators.
when
(*desired_states)¶ Register the decorated function to run when all
desired_states
are active.This decorator will pass zero or more relation instances to the handler, if any of the states are associated with relations. If so, they will be passed in in the same order that the states are given to the decorator.
Note that handlers whose conditions match are triggered at least once per hook invocation.
-
charmhelpers.core.reactive.decorators.
when_file_changed
(*filenames, **kwargs)¶ Register the decorated function to run when one or more files have changed.
Parameters: - filenames (list) – The names of one or more files to check for changes.
- hash_type (str) – The type of hash to use for determining if a file has changed. Defaults to ‘md5’. Must be given as a kwarg.
-
charmhelpers.core.reactive.decorators.
when_not
(*desired_states)¶ Register the decorated function to run when not all desired_states are active.
This decorator will never cause arguments to be passed to the handler.
Note that handlers whose conditions match are triggered at least once per hook invocation.
charmhelpers.core.reactive.helpers¶
any_file_changed |
Check if any of the given files have changed since the last time this was called. |
data_changed |
Check if the given set of data has changed since the previous call. |
mark_invoked |
Mark the given ID as having been invoked, for use with was_invoked() . |
was_invoked |
Returns whether the given ID has been invoked before, as per mark_invoked() . |
-
charmhelpers.core.reactive.helpers.
any_file_changed
(filenames, hash_type='md5')¶ Check if any of the given files have changed since the last time this was called.
Parameters: - filenames (list) – Names of files to check.
- hash_type (str) – Algorithm to use to check the files.
-
charmhelpers.core.reactive.helpers.
data_changed
(data_id, data, hash_type='md5')¶ Check if the given set of data has changed since the previous call.
This works by hashing the JSON-serialization of the data. Note that, while the data will be serialized using
sort_keys=True
, some types of data structures, such as sets, may lead to false positivies.Parameters: - data_id (str) – Unique identifier for this set of data.
- data – JSON-serializable data.
- hash_type (str) – Any hash algorithm supported by
hashlib
.
-
charmhelpers.core.reactive.helpers.
mark_invoked
(invocation_id)¶ Mark the given ID as having been invoked, for use with
was_invoked()
.
-
charmhelpers.core.reactive.helpers.
was_invoked
(invocation_id)¶ Returns whether the given ID has been invoked before, as per
mark_invoked()
.This is useful for ensuring that a given block only runs one time:
def foo(): if was_invoked('foo'): return do_something() mark_invoked('foo')
This is also available as a decorator at
only_once()
.
charmhelpers.core.reactive.relations¶
AutoAccessors |
Metaclass that converts fields referenced by auto_accessors into accessor methods with very basic doc strings. |
Conversation |
Converations are the persistent, evolving, two-way communication between this service and one or more remote services. |
RelationBase |
The base class for all relation implementations. |
relation_call |
Invoke a method on the class implementing a relation via the CLI |
scopes |
These are the recommended scope values for relation implementations. |
-
class
charmhelpers.core.reactive.relations.
AutoAccessors
¶ Bases:
type
Metaclass that converts fields referenced by
auto_accessors
into accessor methods with very basic doc strings.
-
class
charmhelpers.core.reactive.relations.
Conversation
(relation_name=None, units=None, scope=None)¶ Bases:
object
Converations are the persistent, evolving, two-way communication between this service and one or more remote services.
Conversations are not limited to a single Juju hook context. They represent the entire set of interactions between the end-points from the time the relation is joined until it is departed.
Conversations evolve over time, moving from one semantic state to the next as the communication progresses.
Conversations may encompass multiple remote services or units. While a database client would connect to only a single database, that database will likely serve several other services. On the other hand, while the database is only concerned about providing a database to each service as a whole, a load-balancing proxy must consider each unit of each service individually.
Conversations use the idea of
scope
to determine how units and services are grouped together.-
depart
()¶ Remove the current remote unit, for the active hook context, from this conversation. This should be called from a -departed hook.
TODO: Need to figure out a way to have this called implicitly, to ensure cleaning up of conversations that are no longer needed.
-
classmethod
deserialize
(conversation)¶ Deserialize a
serialized
conversation.
-
get_local
(key, default=None)¶ Retrieve some data previously set via
set_local()
for this conversation.
-
get_remote
(key, default=None)¶ Get a value from the remote end(s) of this conversation.
Note that if a conversation’s scope encompasses multiple units, then those units are expected to agree on their data, whether that is through relying on a single leader to set the data or by all units eventually converging to identical data. Thus, this method returns the first value that it finds set by any of its units.
-
classmethod
join
(scope)¶ Get or create a conversation for the given scope and active hook context.
The current remote unit for the active hook context will be added to the conversation.
Note: This uses
charmhelpers.core.unitdata
and requires thatflush()
be called.
-
key
¶ The key under which this conversation will be stored.
-
classmethod
load
(keys)¶ Load a set of conversations by their keys.
-
relation_ids
¶ The set of IDs of the specific relation instances that this conversation is communicating with.
-
remove_state
(state)¶ Remove this conversation from the given state, and potentially deactivate the state if no more conversations are in it.
The relation name will be interpolated in the state name, and it is recommended that it be included to avoid conflicts with states from other relations. For example:
conversation.remove_state('{relation_name}.state')
If called from a converation handling the relation “foo”, this will remove the conversation from the “foo.state” state, and, if no more conversations are in this the state, will deactivate it.
-
classmethod
serialize
(conversation)¶ Serialize a conversation instance for storage.
-
set_local
(key=None, value=None, data=None, **kwdata)¶ Locally store some data associated with this conversation.
Data can be passed in either as a single dict, or as key-word args.
For example, if you need to store the previous value of a remote field to determine if it has changed, you can use the following:
prev = conversation.get_local('field') curr = conversation.get_remote('field') if prev != curr: handle_change(prev, curr) conversation.set_local('field', curr)
Note: This uses
charmhelpers.core.unitdata
and requires thatflush()
be called.Parameters: - key (str) – The name of a field to set.
- value – A value to set.
- data (dict) – A mapping of keys to values.
- **kwdata – A mapping of keys to values, as keyword arguments.
-
set_remote
(key=None, value=None, data=None, **kwdata)¶ Set data for the remote end(s) of this conversation.
Data can be passed in either as a single dict, or as key-word args.
Note that, in Juju, setting relation data is inherently service scoped. That is, if the conversation only includes a single unit, the data will still be set for that unit’s entire service.
However, if this conversation’s scope encompasses multiple services, the data will be set for all of those services.
Parameters: - key (str) – The name of a field to set.
- value – A value to set.
- data (dict) – A mapping of keys to values.
- **kwdata – A mapping of keys to values, as keyword arguments.
-
set_state
(state)¶ Activate and put this conversation into the given state.
The relation name will be interpolated in the state name, and it is recommended that it be included to avoid conflicts with states from other relations. For example:
conversation.set_state('{relation_name}.state')
If called from a converation handling the relation “foo”, this will activate the “foo.state” state, and will add this conversation to that state.
Note: This uses
charmhelpers.core.unitdata
and requires thatflush()
be called.
-
-
class
charmhelpers.core.reactive.relations.
RelationBase
(relation_name, conversations=None)¶ Bases:
object
The base class for all relation implementations.
-
auto_accessors
= []¶ Remote field names to be automatically converted into accessors with basic documentation.
These accessors will just call
get_remote()
using thedefault conversation
. Note that it is highly recommended that this be used only withscopes.GLOBAL
scope.
-
conversation
(scope=None)¶ Get a single conversation, by scope, that this relation is currently handling.
If the scope is not given, the correct scope is inferred by the current hook execution context. If there is no current hook execution context, it is assume that there is only a single global conversation scope for this relation. If this relation’s scope is not global and there is no current hook execution context, then an error is raised.
-
conversations
()¶ Return a list of the conversations that this relation is currently handling.
Note that “currently handling” means for the current state or hook context, and not all conversations that might be active for this relation for other states.
-
classmethod
from_name
(relation_name, conversations=None)¶ Find relation implementation in the current charm, based on the ID of the relation.
Returns: A Relation instance, or None
-
classmethod
from_state
(state)¶ Find relation implementation in the current charm, based on the name of an active state.
-
get_local
(key, default=None, scope=None)¶ Retrieve some data previously set via
set_local()
.In Python, this is equivalent to:
relation.conversation(scope).get_local(key, default)
See
conversation()
andConversation.get_local()
.
-
get_remote
(key, default=None, scope=None)¶ Get data from the remote end(s) of the
Conversation
with the given scope.In Python, this is equivalent to:
relation.conversation(scope).get_remote(key, default)
See
conversation()
andConversation.get_remote()
.
-
relation_name
¶ Name of the relation this instance is handling.
-
remove_state
(state, scope=None)¶ Remove the state for the
Conversation
with the given scope.In Python, this is equivalent to:
relation.conversation(scope).remove_state(state)
-
scope
= 'unit'¶ Conversation scope for this relation.
The conversation scope controls how communication with connected units is aggregated into related
Conversations
, and can be any of the predefinedscopes
, or any arbitrary string. Connected units which share the same scope will be considered part of the same conversation. Data sent to a conversation is sent to all units that are a part of that conversation, and units that are part of a conversation are expected to agree on the data that they send, whether via eventual consistency or by having a single leader set the data.The default scope is
scopes.UNIT
.
-
set_local
(key=None, value=None, data=None, scope=None, **kwdata)¶ Locally store some data, namespaced by the current or given
Conversation
scope.In Python, this is equivalent to:
relation.conversation(scope).set_local(data, scope, **kwdata)
See
conversation()
andConversation.set_local()
.
-
set_remote
(key=None, value=None, data=None, scope=None, **kwdata)¶ Set data for the remote end(s) of the
Conversation
with the given scope.In Python, this is equivalent to:
relation.conversation(scope).set_remote(data, scope, **kwdata)
See
conversation()
andConversation.set_remote()
.
-
set_state
(state, scope=None)¶ Set the state for the
Conversation
with the given scope.In Python, this is equivalent to:
relation.conversation(scope).set_state(state)
See
conversation()
andConversation.set_state()
.
-
-
charmhelpers.core.reactive.relations.
relation_call
(method, relation_name=None, state=None, *args)¶ Invoke a method on the class implementing a relation via the CLI
-
class
charmhelpers.core.reactive.relations.
scopes
¶ Bases:
object
These are the recommended scope values for relation implementations.
To use, simply set the
scope
class variable to one of these:class MyRelationClient(RelationBase): scope = scopes.SERVICE
-
GLOBAL
= 'global'¶ All connected services and units for this relation will share a single conversation. The same data will be broadcast to every remote unit, and retrieved data will be aggregated across all remote units and is expected to either eventually agree or be set by a single leader.
-
SERVICE
= 'service'¶ Each connected service for this relation will have its own conversation. The same data will be broadcast to every unit of each service’s conversation, and data from all units of each service will be aggregated and is expected to either eventually agree or be set by a single leader.
-
UNIT
= 'unit'¶ Each connected unit for this relation will have its own conversation. This is the default scope. Each unit’s data will be retrieved individually, but note that due to how Juju works, the same data is still broadcast to all units of a single service.
-
charmhelpers.core.reactive.bus¶
ExternalHandler |
A variant Handler for external executable actions (such as bash scripts). |
Handler |
Class representing a reactive state handler. |
StateWatch |
|
all_states |
Assert that all desired_states are active |
any_hook |
Assert that the currently executing hook matches one of the given patterns. |
any_states |
Assert that any of the desired_states are active |
discover |
Discover handlers based on convention. |
dispatch |
Dispatch registered handlers. |
get_state |
Return the value associated with an active state, or None |
get_states |
Return a mapping of all active states to their values |
remove_state |
Remove / deactivate a state |
set_state |
Set the given state as active, optionally associating with a relation |
-
class
charmhelpers.core.reactive.bus.
ExternalHandler
(filepath)¶ Bases:
charmhelpers.core.reactive.bus.Handler
A variant Handler for external executable actions (such as bash scripts).
External handlers must adhere to the following protocol:
- The handler can be any executable
- When invoked with the
--test
command-line flag, it should exit with an exit code of zero to indicate that the handler should be invoked, and a non-zero exit code to indicate that it need not be invoked. It can also provide a line of output to be passed to the--invoke
call, e.g., to indicate which sub-handlers should be invoked. The handler should not perform its action when given this flag. - When invoked with the
--invoke
command-line flag (which will be followed by any output returned by the--test
call), the handler should perform its action(s).
-
id
()¶
-
invoke
()¶ Call the external handler to be invoked.
-
classmethod
register
(filepath)¶
-
test
()¶ Call the external handler to test whether it should be invoked.
-
class
charmhelpers.core.reactive.bus.
Handler
(action)¶ Bases:
object
Class representing a reactive state handler.
-
add_args
(args)¶ Add arguments to be passed to the action when invoked.
Parameters: args – Any sequence or iterable, which will be lazily evaluated to provide args. Subsequent calls to add_args()
can be used to add additional arguments.
-
add_predicate
(predicate)¶ Add a new predicate callback to this handler.
-
classmethod
clear
()¶ Clear all registered handlers.
-
classmethod
get
(action)¶ Get or register a handler for the given action.
Parameters: - action (func) – Callback that is called when invoking the Handler
- args_source (func) – Optional callback that generates args for the action
-
classmethod
get_handlers
()¶ Clear all registered handlers.
-
id
()¶
-
invoke
()¶ Invoke this handler.
-
test
()¶ Check the predicate(s) and return True if this handler should be invoked.
-
-
class
charmhelpers.core.reactive.bus.
StateWatch
¶ Bases:
object
-
classmethod
change
(state)¶
-
classmethod
commit
()¶
-
classmethod
iteration
(i)¶
-
key
= 'reactive.state_watch'¶
-
classmethod
reset
()¶
-
classmethod
watch
(watcher, states)¶
-
classmethod
-
charmhelpers.core.reactive.bus.
all_states
(*desired_states)¶ Assert that all desired_states are active
-
charmhelpers.core.reactive.bus.
any_hook
(*hook_patterns)¶ Assert that the currently executing hook matches one of the given patterns.
Each pattern will match one or more hooks, and can use the following special syntax:
db-relation-{joined,changed}
can be used to match multiple hooks (in this case,db-relation-joined
anddb-relation-changed
).{provides:mysql}-relation-joined
can be used to match a relation hook by the role and interface instead of the relation name. The role must be one ofprovides
,requires
, orpeer
.- The previous two can be combined, of course:
{provides:mysql}-relation-{joined,changed}
-
charmhelpers.core.reactive.bus.
any_states
(*desired_states)¶ Assert that any of the desired_states are active
-
charmhelpers.core.reactive.bus.
discover
()¶ Discover handlers based on convention.
Handlers will be loaded from the following directories and their subdirectories:
$CHARM_DIR/reactive/
$CHARM_DIR/hooks/reactive/
$CHARM_DIR/hooks/relations/
They can be Python files, in which case they will be imported and decorated functions registered. Or they can be executables, in which case they must adhere to the
ExternalHandler
protocol.
-
charmhelpers.core.reactive.bus.
dispatch
()¶ Dispatch registered handlers.
Handlers are dispatched according to the following rules:
- Handlers are repeatedly tested and invoked in iterations, until the system settles into quiescence (that is, until no new handlers match to be invoked).
- In the first iteration,
@hook
and@action
handlers will be invoked, if they match. - In subsequent iterations, other handlers are invoked, if they match.
- Added states will not trigger new handlers until the next iteration, to ensure that chained states are invoked in a predictable order.
- Removed states will cause the current set of matched handlers to be re-tested, to ensure that no handler is invoked after its matching state has been removed.
- Other than the guarantees mentioned above, the order in which matching handlers are invoked is undefined.
- States are preserved between hook and action invocations, and all matching
handlers are re-invoked for every hook and action. There are
decorators and
helpers
to prevent unnecessary reinvocations, such as
only_once()
.
-
charmhelpers.core.reactive.bus.
get_state
(state, default=None)¶ Return the value associated with an active state, or None
-
charmhelpers.core.reactive.bus.
get_states
()¶ Return a mapping of all active states to their values
-
charmhelpers.core.reactive.bus.
remove_state
(state)¶ Remove / deactivate a state
-
charmhelpers.core.reactive.bus.
set_state
(state, value=None)¶ Set the given state as active, optionally associating with a relation
-
charmhelpers.core.reactive.
main
(relation_name=None)¶ This is the main entry point for the reactive framework. It calls
discover()
to find and load all reactive handlers (e.g.,@when
decorated blocks), and thendispatch()
to trigger hook and state handlers until the state settles out. Finally,unitdata.kv().flush
is called to persist the state.Parameters: relation_name (str) – Optional name of the relation which is being handled.
charmhelpers.core.decorators¶
-
charmhelpers.core.decorators.
retry_on_exception
(num_retries, base_delay=0, exc_type=<type 'exceptions.Exception'>)¶ If the decorated function raises exception exc_type, allow num_retries retry attempts before raise the exception.
charmhelpers.core.fstab¶
-
class
charmhelpers.core.fstab.
Fstab
(path=None)¶ Bases:
_io.FileIO
This class extends file in order to implement a file reader/writer for file /etc/fstab
-
DEFAULT_PATH
= '/etc/fstab'¶
-
class
Entry
(device, mountpoint, filesystem, options, d=0, p=0)¶ Bases:
object
Entry class represents a non-comment line on the /etc/fstab file
-
classmethod
Fstab.
add
(device, mountpoint, filesystem, options=None, path=None)¶
-
Fstab.
add_entry
(entry)¶
-
Fstab.
entries
¶
-
Fstab.
get_entry_by_attr
(attr, value)¶
-
classmethod
Fstab.
remove_by_mountpoint
(mountpoint, path=None)¶
-
Fstab.
remove_entry
(entry)¶
-
charmhelpers.core.hookenv¶
Config |
A dictionary representation of the charm’s config.yaml, with some |
Hooks |
A convenient handler for hook functions. |
Serializable |
Wrapper, an object that can be serialized to yaml or json |
UnregisteredHookError |
Raised when an undefined hook is called |
action_fail |
Sets the action status to failed and sets the error message. |
action_get |
Gets the value of an action parameter, or all key/value param pairs |
action_set |
Sets the values to be returned after the action finishes |
atexit |
Schedule a callback to run on successful hook completion. |
atstart |
Schedule a callback to run before the main hook. |
cached |
Cache return values for multiple executions of func + args |
charm_dir |
Return the root directory of the current charm |
charm_name |
Get the name of the current charm as is specified on metadata.yaml |
close_port |
Close a service network port |
config |
Juju charm configuration |
execution_environment |
A convenient bundling of the current execution context |
flush |
Flushes any entries from function cache where the |
has_juju_version |
Return True if the Juju version is at least the provided version |
hook_name |
The name of the currently executing hook |
in_relation_hook |
Determine whether we’re running in a relation hook |
interface_to_relations |
Given an interface, return a list of relation names for the current charm that use that interface. |
is_leader |
|
is_relation_made |
Determine whether a relation is established by checking for presence of key(s). |
juju_version |
Full version string (eg. |
leader_get |
|
leader_set |
|
local_unit |
Local unit ID |
log |
Write a message to the juju log |
metadata |
Get the current charm metadata.yaml contents as a python object |
open_port |
Open a service network port |
related_units |
A list of related units |
relation_clear |
Clears any relation data already set on relation r_id |
relation_for_unit |
Get the json represenation of a unit’s relation |
relation_get |
Get relation information |
relation_id |
The relation ID for the current or a specified relation |
relation_ids |
A list of relation_ids |
relation_set |
Set relation information for the current unit |
relation_to_interface |
Given the name of a relation, return the interface that relation uses. |
relation_to_role_and_interface |
Given the name of a relation, return the role and the name of the interface that relation uses (where role is one of provides , requires , or peer ). |
relation_type |
The scope for the current relation hook |
relation_types |
Get a list of relation types supported by this charm |
relations |
Get a nested dictionary of relation data for all related units |
relations_for_id |
Get relations of a specific relation ID |
relations_of_type |
Get relations of a specific type |
remote_service_name |
The remote service name for a given relation-id (or the current relation) |
remote_unit |
The remote unit for the current relation hook |
role_and_interface_to_relations |
Given a role and interface name, return a list of relation names for the current charm that use that interface under that role (where role is one of provides , requires , or peer ). |
service_name |
The name service group this unit belongs to |
status_get |
Retrieve the previously set juju workload state |
status_set |
Set the workload state with a message |
translate_exc |
|
unit_get |
Get the unit ID for the remote unit |
unit_private_ip |
Get this unit’s private IP address |
unit_public_ip |
Get this unit’s public IP address |
Interactions with the Juju environment
-
class
charmhelpers.core.hookenv.
Config
(*args, **kw)¶ Bases:
dict
A dictionary representation of the charm’s config.yaml, with some extra features:
- See which values in the dictionary have changed since the previous hook.
- For values that have changed, see what the previous value was.
- Store arbitrary data for use in a later hook.
NOTE: Do not instantiate this object directly - instead call
hookenv.config()
, which will return an instance ofConfig
.Example usage:
>>> # inside a hook >>> from charmhelpers.core import hookenv >>> config = hookenv.config() >>> config['foo'] 'bar' >>> # store a new key/value for later use >>> config['mykey'] = 'myval' >>> # user runs `juju set mycharm foo=baz` >>> # now we're inside subsequent config-changed hook >>> config = hookenv.config() >>> config['foo'] 'baz' >>> # test to see if this val has changed since last hook >>> config.changed('foo') True >>> # what was the previous value? >>> config.previous('foo') 'bar' >>> # keys/values that we add are preserved across hooks >>> config['mykey'] 'myval'
-
CONFIG_FILE_NAME
= '.juju-persistent-config'¶
-
changed
(key)¶ Return True if the current value for this key is different from the previous value.
-
load_previous
(path=None)¶ Load previous copy of config from disk.
In normal usage you don’t need to call this method directly - it is called automatically at object initialization.
Parameters: path – File path from which to load the previous config. If None, config is loaded from the default location. If path is specified, subsequent save() calls will write to the same path.
-
previous
(key)¶ Return previous value for this key, or None if there is no previous value.
-
save
()¶ Save this config to disk.
If the charm is using the
Services Framework
or :meth:’@hook <Hooks.hook>’ decorator, this is called automatically at the end of successful hook execution. Otherwise, it should be called directly by user code.To disable automatic saves, set
implicit_save=False
on this instance.
-
class
charmhelpers.core.hookenv.
Hooks
(config_save=None)¶ Bases:
object
A convenient handler for hook functions.
Example:
hooks = Hooks() # register a hook, taking its name from the function name @hooks.hook() def install(): pass # your code here # register a hook, providing a custom hook name @hooks.hook("config-changed") def config_changed(): pass # your code here if __name__ == "__main__": # execute a hook based on the name the program is called by hooks.execute(sys.argv)
-
execute
(args)¶ Execute a registered hook based on args[0]
-
hook
(*hook_names)¶ Decorator, registering them as hooks
-
register
(name, function)¶ Register a hook
-
-
class
charmhelpers.core.hookenv.
Serializable
(obj)¶ Bases:
UserDict.UserDict
Wrapper, an object that can be serialized to yaml or json
-
json
()¶ Serialize the object to json
-
yaml
()¶ Serialize the object to yaml
-
-
exception
charmhelpers.core.hookenv.
UnregisteredHookError
¶ Bases:
exceptions.Exception
Raised when an undefined hook is called
-
charmhelpers.core.hookenv.
action_fail
(message)¶ Sets the action status to failed and sets the error message.
The results set by action_set are preserved.
-
charmhelpers.core.hookenv.
action_get
(*args, **kwargs)¶ Gets the value of an action parameter, or all key/value param pairs
-
charmhelpers.core.hookenv.
action_set
(values)¶ Sets the values to be returned after the action finishes
-
charmhelpers.core.hookenv.
atexit
(callback, *args, **kwargs)¶ Schedule a callback to run on successful hook completion.
Callbacks are run in the reverse order that they were added.
-
charmhelpers.core.hookenv.
atstart
(callback, *args, **kwargs)¶ Schedule a callback to run before the main hook.
Callbacks are run in the order they were added.
This is useful for modules and classes to perform initialization and inject behavior. In particular:
- Run common code before all of your hooks, such as logging the hook name or interesting relation data.
- Defer object or module initialization that requires a hook context until we know there actually is a hook context, making testing easier.
- Rather than requiring charm authors to include boilerplate to invoke your helper’s behavior, have it run automatically if your object is instantiated or module imported.
This is not at all useful after your hook framework as been launched.
-
charmhelpers.core.hookenv.
cached
(func)¶ Cache return values for multiple executions of func + args
For example:
@cached def unit_get(attribute): pass unit_get('test')
will cache the result of unit_get + ‘test’ for future calls.
-
charmhelpers.core.hookenv.
charm_dir
()¶ Return the root directory of the current charm
-
charmhelpers.core.hookenv.
charm_name
(*args, **kwargs)¶ Get the name of the current charm as is specified on metadata.yaml
-
charmhelpers.core.hookenv.
close_port
(port, protocol='TCP')¶ Close a service network port
-
charmhelpers.core.hookenv.
config
(*args, **kwargs)¶ Juju charm configuration
-
charmhelpers.core.hookenv.
execution_environment
()¶ A convenient bundling of the current execution context
-
charmhelpers.core.hookenv.
flush
(key)¶ Flushes any entries from function cache where the key is found in the function+args
-
charmhelpers.core.hookenv.
has_juju_version
(*args, **kwargs)¶ Return True if the Juju version is at least the provided version
-
charmhelpers.core.hookenv.
hook_name
()¶ The name of the currently executing hook
-
charmhelpers.core.hookenv.
in_relation_hook
()¶ Determine whether we’re running in a relation hook
-
charmhelpers.core.hookenv.
interface_to_relations
(*args, **kwargs)¶ Given an interface, return a list of relation names for the current charm that use that interface.
Returns: A list of relation names.
-
charmhelpers.core.hookenv.
is_leader
(*args, **kwargs)¶
-
charmhelpers.core.hookenv.
is_relation_made
(*args, **kwargs)¶ Determine whether a relation is established by checking for presence of key(s). If a list of keys is provided, they must all be present for the relation to be identified as made
-
charmhelpers.core.hookenv.
juju_version
(*args, **kwargs)¶ Full version string (eg. ‘1.23.3.1-trusty-amd64’)
-
charmhelpers.core.hookenv.
leader_get
(*args, **kwargs)¶
-
charmhelpers.core.hookenv.
leader_set
(*args, **kwargs)¶
-
charmhelpers.core.hookenv.
local_unit
()¶ Local unit ID
-
charmhelpers.core.hookenv.
log
(message, level=None)¶ Write a message to the juju log
-
charmhelpers.core.hookenv.
metadata
(*args, **kwargs)¶ Get the current charm metadata.yaml contents as a python object
-
charmhelpers.core.hookenv.
open_port
(port, protocol='TCP')¶ Open a service network port
A list of related units
-
charmhelpers.core.hookenv.
relation_clear
(r_id=None)¶ Clears any relation data already set on relation r_id
-
charmhelpers.core.hookenv.
relation_for_unit
(*args, **kwargs)¶ Get the json represenation of a unit’s relation
-
charmhelpers.core.hookenv.
relation_get
(*args, **kwargs)¶ Get relation information
-
charmhelpers.core.hookenv.
relation_id
(*args, **kwargs)¶ The relation ID for the current or a specified relation
-
charmhelpers.core.hookenv.
relation_ids
(*args, **kwargs)¶ A list of relation_ids
-
charmhelpers.core.hookenv.
relation_set
(relation_id=None, relation_settings=None, **kwargs)¶ Set relation information for the current unit
-
charmhelpers.core.hookenv.
relation_to_interface
(*args, **kwargs)¶ Given the name of a relation, return the interface that relation uses.
Returns: The interface name, or None
.
-
charmhelpers.core.hookenv.
relation_to_role_and_interface
(*args, **kwargs)¶ Given the name of a relation, return the role and the name of the interface that relation uses (where role is one of
provides
,requires
, orpeer
).Returns: A tuple containing (role, interface)
, or(None, None)
.
-
charmhelpers.core.hookenv.
relation_type
()¶ The scope for the current relation hook
-
charmhelpers.core.hookenv.
relation_types
(*args, **kwargs)¶ Get a list of relation types supported by this charm
-
charmhelpers.core.hookenv.
relations
(*args, **kwargs)¶ Get a nested dictionary of relation data for all related units
-
charmhelpers.core.hookenv.
relations_for_id
(*args, **kwargs)¶ Get relations of a specific relation ID
-
charmhelpers.core.hookenv.
relations_of_type
(*args, **kwargs)¶ Get relations of a specific type
-
charmhelpers.core.hookenv.
remote_service_name
(*args, **kwargs)¶ The remote service name for a given relation-id (or the current relation)
-
charmhelpers.core.hookenv.
remote_unit
()¶ The remote unit for the current relation hook
-
charmhelpers.core.hookenv.
role_and_interface_to_relations
(*args, **kwargs)¶ Given a role and interface name, return a list of relation names for the current charm that use that interface under that role (where role is one of
provides
,requires
, orpeer
).Returns: A list of relation names.
-
charmhelpers.core.hookenv.
service_name
()¶ The name service group this unit belongs to
-
charmhelpers.core.hookenv.
status_get
()¶ Retrieve the previously set juju workload state
If the status-set command is not found then assume this is juju < 1.23 and return ‘unknown’
-
charmhelpers.core.hookenv.
status_set
(workload_state, message)¶ Set the workload state with a message
Use status-set to set the workload state with a message which is visible to the user via juju status. If the status-set command is not found then assume this is juju < 1.23 and juju-log the message unstead.
workload_state – valid juju workload state. message – status update message
-
charmhelpers.core.hookenv.
translate_exc
(from_exc, to_exc)¶
-
charmhelpers.core.hookenv.
unit_get
(*args, **kwargs)¶ Get the unit ID for the remote unit
-
charmhelpers.core.hookenv.
unit_private_ip
()¶ Get this unit’s private IP address
-
charmhelpers.core.hookenv.
unit_public_ip
()¶ Get this unit’s public IP address
charmhelpers.core.host¶
ChecksumError |
|
add_group |
Add a group to the system |
add_user_to_group |
Add a user to a group |
adduser |
Add a user to the system |
chdir |
|
check_hash |
Validate a file using a cryptographic checksum. |
chownr |
|
cmp_pkgrevno |
Compare supplied revno with the revno of the installed package |
file_hash |
Generate a hash checksum of the contents of ‘path’ or None if not found. |
fstab_add |
Adds the given device entry to the /etc/fstab file |
fstab_remove |
Remove the given mountpoint entry from /etc/fstab |
get_nic_hwaddr |
|
get_nic_mtu |
|
lchownr |
|
list_nics |
Return a list of nics of given type(s) |
lsb_release |
Return /etc/lsb-release in a dict |
mkdir |
Create a directory |
mount |
Mount a filesystem at a particular mountpoint |
mounts |
Get a list of all mounted volumes as [[mountpoint,device],[...]] |
path_hash |
Generate a hash checksum of all files matching ‘path’. |
pwgen |
Generate a random pasword. |
restart_on_change |
Restart services based on configuration files changing |
rsync |
Replicate the contents of a path |
service |
Control a system service |
service_available |
Determine whether a system service is available |
service_reload |
Reload a system service, optionally falling back to restart if |
service_restart |
Restart a system service |
service_running |
Determine whether a system service is running |
service_start |
Start a system service |
service_stop |
Stop a system service |
set_nic_mtu |
Set MTU on a network interface |
symlink |
Create a symbolic link |
umount |
Unmount a filesystem |
write_file |
Create or overwrite a file with the contents of a byte string. |
Tools for working with the host system
-
exception
charmhelpers.core.host.
ChecksumError
¶ Bases:
exceptions.ValueError
-
charmhelpers.core.host.
add_group
(group_name, system_group=False)¶ Add a group to the system
-
charmhelpers.core.host.
add_user_to_group
(username, group)¶ Add a user to a group
-
charmhelpers.core.host.
adduser
(username, password=None, shell='/bin/bash', system_user=False)¶ Add a user to the system
-
charmhelpers.core.host.
chdir
(*args, **kwds)¶
-
charmhelpers.core.host.
check_hash
(path, checksum, hash_type='md5')¶ Validate a file using a cryptographic checksum.
Parameters: - checksum (str) – Value of the checksum used to validate the file.
- hash_type (str) – Hash algorithm used to generate checksum.
Can be any hash alrgorithm supported by
hashlib
, such as md5, sha1, sha256, sha512, etc.
Raises: ChecksumError – If the file fails the checksum
-
charmhelpers.core.host.
chownr
(path, owner, group, follow_links=True)¶
-
charmhelpers.core.host.
cmp_pkgrevno
(package, revno, pkgcache=None)¶ Compare supplied revno with the revno of the installed package
- 1 => Installed revno is greater than supplied arg
- 0 => Installed revno is the same as supplied arg
- -1 => Installed revno is less than supplied arg
This function imports apt_cache function from charmhelpers.fetch if the pkgcache argument is None. Be sure to add charmhelpers.fetch if you call this function, or pass an apt_pkg.Cache() instance.
-
charmhelpers.core.host.
file_hash
(path, hash_type='md5')¶ Generate a hash checksum of the contents of ‘path’ or None if not found.
Parameters: hash_type (str) – Any hash alrgorithm supported by hashlib
, such as md5, sha1, sha256, sha512, etc.
-
charmhelpers.core.host.
fstab_add
(dev, mp, fs, options=None)¶ Adds the given device entry to the /etc/fstab file
-
charmhelpers.core.host.
fstab_remove
(mp)¶ Remove the given mountpoint entry from /etc/fstab
-
charmhelpers.core.host.
get_nic_hwaddr
(nic)¶
-
charmhelpers.core.host.
get_nic_mtu
(nic)¶
-
charmhelpers.core.host.
lchownr
(path, owner, group)¶
-
charmhelpers.core.host.
list_nics
(nic_type)¶ Return a list of nics of given type(s)
-
charmhelpers.core.host.
lsb_release
()¶ Return /etc/lsb-release in a dict
-
charmhelpers.core.host.
mkdir
(path, owner='root', group='root', perms=365, force=False)¶ Create a directory
-
charmhelpers.core.host.
mount
(device, mountpoint, options=None, persist=False, filesystem='ext3')¶ Mount a filesystem at a particular mountpoint
-
charmhelpers.core.host.
mounts
()¶ Get a list of all mounted volumes as [[mountpoint,device],[...]]
-
charmhelpers.core.host.
path_hash
(path)¶ Generate a hash checksum of all files matching ‘path’. Standard wildcards like ‘*’ and ‘?’ are supported, see documentation for the ‘glob’ module for more information.
Returns: dict: A { filename: hash } dictionary for all matched files. Empty if none found.
-
charmhelpers.core.host.
pwgen
(length=None)¶ Generate a random pasword.
-
charmhelpers.core.host.
restart_on_change
(restart_map, stopstart=False)¶ Restart services based on configuration files changing
This function is used a decorator, for example:
@restart_on_change({ '/etc/ceph/ceph.conf': [ 'cinder-api', 'cinder-volume' ] '/etc/apache/sites-enabled/*': [ 'apache2' ] }) def config_changed(): pass # your code here
In this example, the cinder-api and cinder-volume services would be restarted if /etc/ceph/ceph.conf is changed by the ceph_client_changed function. The apache2 service would be restarted if any file matching the pattern got changed, created or removed. Standard wildcards are supported, see documentation for the ‘glob’ module for more information.
-
charmhelpers.core.host.
rsync
(from_path, to_path, flags='-r', options=None)¶ Replicate the contents of a path
-
charmhelpers.core.host.
service
(action, service_name)¶ Control a system service
-
charmhelpers.core.host.
service_available
(service_name)¶ Determine whether a system service is available
-
charmhelpers.core.host.
service_reload
(service_name, restart_on_failure=False)¶ Reload a system service, optionally falling back to restart if reload fails
-
charmhelpers.core.host.
service_restart
(service_name)¶ Restart a system service
-
charmhelpers.core.host.
service_running
(service)¶ Determine whether a system service is running
-
charmhelpers.core.host.
service_start
(service_name)¶ Start a system service
-
charmhelpers.core.host.
service_stop
(service_name)¶ Stop a system service
-
charmhelpers.core.host.
set_nic_mtu
(nic, mtu)¶ Set MTU on a network interface
-
charmhelpers.core.host.
symlink
(source, destination)¶ Create a symbolic link
-
charmhelpers.core.host.
umount
(mountpoint, persist=False)¶ Unmount a filesystem
-
charmhelpers.core.host.
write_file
(path, content, owner='root', group='root', perms=292)¶ Create or overwrite a file with the contents of a byte string.
charmhelpers.core.strutils¶
-
charmhelpers.core.strutils.
bool_from_string
(value)¶ Interpret string value as boolean.
Returns True if value translates to True otherwise False.
charmhelpers.core.sysctl¶
-
charmhelpers.core.sysctl.
create
(sysctl_dict, sysctl_file)¶ Creates a sysctl.conf file from a YAML associative array
Parameters: - sysctl_dict (str) – a YAML-formatted string of sysctl options eg “{ ‘kernel.max_pid’: 1337 }”
- sysctl_file (str or unicode) – path to the sysctl file to be saved
Returns: None
charmhelpers.core.templating¶
-
charmhelpers.core.templating.
render
(source, target, context, owner='root', group='root', perms=292, templates_dir=None, encoding='UTF-8')¶ Render a template.
The source path, if not absolute, is relative to the templates_dir.
The target path should be absolute.
The context should be a dict containing the values to be replaced in the template.
The owner, group, and perms options will be passed to write_file.
If omitted, templates_dir defaults to the templates folder in the charm.
Note: Using this requires python-jinja2; if it is not installed, calling this will attempt to use charmhelpers.fetch.apt_install to install it.
charmhelpers.core.unitdata¶
Intro¶
A simple way to store state in units. This provides a key value storage with support for versioned, transactional operation, and can calculate deltas from previous values to simplify unit logic when processing changes.
Hook Integration¶
There are several extant frameworks for hook execution, including
- charmhelpers.core.hookenv.Hooks
- charmhelpers.core.services.ServiceManager
The storage classes are framework agnostic, one simple integration is via the HookData contextmanager. It will record the current hook execution environment (including relation data, config data, etc.), setup a transaction and allow easy access to the changes from previously seen values. One consequence of the integration is the reservation of particular keys (‘rels’, ‘unit’, ‘env’, ‘config’, ‘charm_revisions’) for their respective values.
Here’s a fully worked integration example using hookenv.Hooks:
from charmhelper.core import hookenv, unitdata
hook_data = unitdata.HookData()
db = unitdata.kv()
hooks = hookenv.Hooks()
@hooks.hook
def config_changed():
# Print all changes to configuration from previously seen
# values.
for changed, (prev, cur) in hook_data.conf.items():
print('config changed', changed,
'previous value', prev,
'current value', cur)
# Get some unit specific bookeeping
if not db.get('pkg_key'):
key = urllib.urlopen('https://example.com/pkg_key').read()
db.set('pkg_key', key)
# Directly access all charm config as a mapping.
conf = db.getrange('config', True)
# Directly access all relation data as a mapping
rels = db.getrange('rels', True)
if __name__ == '__main__':
with hook_data():
hook.execute()
A more basic integration is via the hook_scope context manager which simply manages transaction scope (and records hook name, and timestamp):
>>> from unitdata import kv
>>> db = kv()
>>> with db.hook_scope('install'):
... # do work, in transactional scope.
... db.set('x', 1)
>>> db.get('x')
1
Usage¶
Values are automatically json de/serialized to preserve basic typing and complex data struct capabilities (dicts, lists, ints, booleans, etc).
Individual values can be manipulated via get/set:
>>> kv.set('y', True)
>>> kv.get('y')
True
# We can set complex values (dicts, lists) as a single key.
>>> kv.set('config', {'a': 1, 'b': True'})
# Also supports returning dictionaries as a record which
# provides attribute access.
>>> config = kv.get('config', record=True)
>>> config.b
True
Groups of keys can be manipulated with update/getrange:
>>> kv.update({'z': 1, 'y': 2}, prefix="gui.")
>>> kv.getrange('gui.', strip=True)
{'z': 1, 'y': 2}
When updating values, its very helpful to understand which values have actually changed and how have they changed. The storage provides a delta method to provide for this:
>>> data = {'debug': True, 'option': 2}
>>> delta = kv.delta(data, 'config.')
>>> delta.debug.previous
None
>>> delta.debug.current
True
>>> delta
{'debug': (None, True), 'option': (None, 2)}
Note the delta method does not persist the actual change, it needs to be explicitly saved via ‘update’ method:
>>> kv.update(data, 'config.')
Values modified in the context of a hook scope retain historical values associated to the hookname.
>>> with db.hook_scope('config-changed'):
... db.set('x', 42)
>>> db.gethistory('x')
[(1, u'x', 1, u'install', u'2015-01-21T16:49:30.038372'),
(2, u'x', 42, u'config-changed', u'2015-01-21T16:49:30.038786')]
-
class
charmhelpers.core.unitdata.
Delta
(previous, current)¶ Bases:
tuple
-
current
¶ Alias for field number 1
-
previous
¶ Alias for field number 0
-
-
class
charmhelpers.core.unitdata.
DeltaSet
¶
-
class
charmhelpers.core.unitdata.
HookData
¶ Bases:
object
Simple integration for existing hook exec frameworks.
Records all unit information, and stores deltas for processing by the hook.
Sample:
from charmhelper.core import hookenv, unitdata changes = unitdata.HookData() db = unitdata.kv() hooks = hookenv.Hooks() @hooks.hook def config_changed(): # View all changes to configuration for changed, (prev, cur) in changes.conf.items(): print('config changed', changed, 'previous value', prev, 'current value', cur) # Get some unit specific bookeeping if not db.get('pkg_key'): key = urllib.urlopen('https://example.com/pkg_key').read() db.set('pkg_key', key) if __name__ == '__main__': with changes(): hook.execute()
-
class
charmhelpers.core.unitdata.
Record
¶ Bases:
dict
-
class
charmhelpers.core.unitdata.
Storage
(path=None)¶ Bases:
object
Simple key value database for local unit state within charms.
Modifications are not persisted unless
flush()
is called.To support dicts, lists, integer, floats, and booleans values are automatically json encoded/decoded.
-
close
()¶
-
debug
(fh=<open file '<stderr>', mode 'w'>)¶
-
delta
(mapping, prefix)¶ return a delta containing values that have changed.
-
flush
(save=True)¶
-
get
(key, default=None, record=False)¶
-
gethistory
(key, deserialize=False)¶
-
getrange
(key_prefix, strip=False)¶ Get a range of keys starting with a common prefix as a mapping of keys to values.
Parameters: - key_prefix (str) – Common prefix among all keys
- strip (bool) – Optionally strip the common prefix from the key names in the returned dict
Return dict: A (possibly empty) dict of key-value mappings
-
hook_scope
(*args, **kwds)¶ Scope all future interactions to the current hook execution revision.
-
set
(key, value)¶ Set a value in the database.
Parameters: - key (str) – Key to set the value for
- value – Any JSON-serializable value to be set
-
unset
(key)¶ Remove a key from the database entirely.
-
unsetrange
(keys=None, prefix='')¶ Remove a range of keys starting with a common prefix, from the database entirely.
Parameters: - keys (list) – List of keys to remove.
- prefix (str) – Optional prefix to apply to all keys in
keys
before removing.
-
update
(mapping, prefix='')¶ Set the values of multiple keys at once.
Parameters: - mapping (dict) – Mapping of keys to values
- prefix (str) – Optional prefix to apply to all keys in mapping before setting
-
-
charmhelpers.core.unitdata.
kv
()¶
charmhelpers.core.services [DEPRECATED]¶
charmhelpers.core.services.base¶
ManagerCallback |
Special case of a callback that takes the ServiceManager instance in addition to the service name. |
PortManagerCallback |
Callback class that will open or close ports, for use as either a start or stop action. |
ServiceManager |
|
close_ports |
Callback class that will open or close ports, for use as either a start or stop action. |
manage_ports |
Callback class that will open or close ports, for use as either a start or stop action. |
open_ports |
Callback class that will open or close ports, for use as either a start or stop action. |
service_restart |
Wrapper around host.service_restart to prevent spurious “unknown service” messages in the logs. |
service_stop |
Wrapper around host.service_stop to prevent spurious “unknown service” messages in the logs. |
-
class
charmhelpers.core.services.base.
ServiceManager
(services=None)¶ Bases:
object
-
fire_event
(event_name, service_name, default=None)¶ Fire a data_ready, data_lost, start, or stop event on a given service.
-
get_service
(service_name)¶ Given the name of a registered service, return its service definition.
-
is_ready
(service_name)¶ Determine if a registered service is ready, by checking its ‘required_data’.
A ‘required_data’ item can be any mapping type, and is considered ready if bool(item) evaluates as True.
-
manage
()¶ Handle the current hook by doing The Right Thing with the registered services.
-
provide_data
()¶ Set the relation data for each provider in the
provided_data
list.A provider must have a name attribute, which indicates which relation to set data on, and a provide_data() method, which returns a dict of data to set.
The provide_data() method can optionally accept two parameters:
remote_service
The name of the remote service that the data will be provided to. The provide_data() method will be called once for each connected service (not unit). This allows the method to tailor its data to the given service.service_ready
Whether or not the service definition had all of its requirements met, and thus thedata_ready
callbacks run.
Note that the
provided_data
methods are now called after thedata_ready
callbacks are run. This gives thedata_ready
callbacks a chance to generate any data necessary for the providing to the remote services.
-
reconfigure_services
(*service_names)¶ Update all files for one or more registered services, and, if ready, optionally restart them.
If no service names are given, reconfigures all registered services.
-
save_lost
(service_name)¶ Save an indicator that the given service is no longer data_ready.
-
save_ready
(service_name)¶ Save an indicator that the given service is now data_ready.
-
stop_services
(*service_names)¶ Stop one or more registered services, by name.
If no service names are given, stops all registered services.
-
was_ready
(service_name)¶ Determine if the given service was previously data_ready.
-
-
class
charmhelpers.core.services.base.
ManagerCallback
¶ Bases:
object
Special case of a callback that takes the ServiceManager instance in addition to the service name.
Subclasses should implement __call__ which should accept three parameters:
- manager The ServiceManager instance
- service_name The name of the service it’s being triggered for
- event_name The name of the event that this callback is handling
-
class
charmhelpers.core.services.base.
PortManagerCallback
¶ Bases:
charmhelpers.core.services.base.ManagerCallback
Callback class that will open or close ports, for use as either a start or stop action.
-
charmhelpers.core.services.base.
service_restart
(service_name)¶ Wrapper around host.service_restart to prevent spurious “unknown service” messages in the logs.
-
charmhelpers.core.services.base.
service_stop
(service_name)¶ Wrapper around host.service_stop to prevent spurious “unknown service” messages in the logs.
charmhelpers.core.services.helpers¶
HttpRelation |
Relation context for the http interface. |
MysqlRelation |
Relation context for the mysql interface. |
RelationContext |
Base class for a context generator that gets relation data from juju. |
RequiredConfig |
Data context that loads config options with one or more mandatory options. |
StoredContext |
A data context that always returns the data that it was first created with. |
TemplateCallback |
Callback class that will render a Jinja2 template, for use as a ready action. |
render_template |
alias of TemplateCallback |
template |
alias of TemplateCallback |
-
class
charmhelpers.core.services.helpers.
RelationContext
(name=None, additional_required_keys=None)¶ Bases:
dict
Base class for a context generator that gets relation data from juju.
Subclasses must provide the attributes name, which is the name of the interface of interest, interface, which is the type of the interface of interest, and required_keys, which is the set of keys required for the relation to be considered complete. The data for all interfaces matching the name attribute that are complete will used to populate the dictionary values (see get_data, below).
The generated context will be namespaced under the relation
name
, to prevent potential naming conflicts.Parameters: - name (str) – Override the relation
name
, since it can vary from charm to charm - additional_required_keys (list) – Extend the list of
required_keys
-
get_data
()¶ Retrieve the relation data for each unit involved in a relation and, if complete, store it in a list under self[self.name]. This is automatically called when the RelationContext is instantiated.
The units are sorted lexographically first by the service ID, then by the unit ID. Thus, if an interface has two other services, ‘db:1’ and ‘db:2’, with ‘db:1’ having two units, ‘wordpress/0’ and ‘wordpress/1’, and ‘db:2’ having one unit, ‘mediawiki/0’, all of which have a complete set of data, the relation data for the units will be stored in the order: ‘wordpress/0’, ‘wordpress/1’, ‘mediawiki/0’.
If you only care about a single unit on the relation, you can just access it as {{ interface[0][‘key’] }}. However, if you can at all support multiple units on a relation, you should iterate over the list, like:
{% for unit in interface -%} {{ unit['key'] }}{% if not loop.last %},{% endif %} {%- endfor %}
Note that since all sets of relation data from all related services and units are in a single list, if you need to know which service or unit a set of data came from, you’ll need to extend this class to preserve that information.
-
interface
= None¶
-
is_ready
()¶ Returns True if all of the required_keys are available from any units.
-
name
= None¶
-
provide_data
()¶ Return data to be relation_set for this interface.
- name (str) – Override the relation
-
class
charmhelpers.core.services.helpers.
TemplateCallback
(source, target, owner='root', group='root', perms=292)¶ Bases:
charmhelpers.core.services.base.ManagerCallback
Callback class that will render a Jinja2 template, for use as a ready action.
Parameters: - source (str) – The template source file, relative to $CHARM_DIR/templates
- target (str) – The target to write the rendered template to
- owner (str) – The owner of the rendered file
- group (str) – The group of the rendered file
- perms (int) – The permissions of the rendered file
-
charmhelpers.core.services.helpers.
render_template
¶ alias of
TemplateCallback
-
charmhelpers.core.services.helpers.
template
¶ alias of
TemplateCallback
charmhelpers.contrib package¶
charmhelpers.contrib.ansible package¶
Charm Helpers ansible - declare the state of your machines.
This helper enables you to declare your machine state, rather than program it procedurally (and have to test each change to your procedures). Your install hook can be as simple as:
{{{
import charmhelpers.contrib.ansible
def install():
charmhelpers.contrib.ansible.install_ansible_support()
charmhelpers.contrib.ansible.apply_playbook('playbooks/install.yaml')
}}}
and won’t need to change (nor will its tests) when you change the machine state.
All of your juju config and relation-data are available as template variables within your playbooks and templates. An install playbook looks something like:
{{{
---
- hosts: localhost
user: root
tasks:
- name: Add private repositories.
template:
src: ../templates/private-repositories.list.jinja2
dest: /etc/apt/sources.list.d/private.list
- name: Update the cache.
apt: update_cache=yes
- name: Install dependencies.
apt: pkg={{ item }}
with_items:
- python-mimeparse
- python-webob
- sunburnt
- name: Setup groups.
group: name={{ item.name }} gid={{ item.gid }}
with_items:
- { name: 'deploy_user', gid: 1800 }
- { name: 'service_user', gid: 1500 }
...
}}}
Read more online about playbooks and standard ansible modules.
A further feature os the ansible hooks is to provide a light weight “action” scripting tool. This is a decorator that you apply to a function, and that function can now receive cli args, and can pass extra args to the playbook.
e.g.
@hooks.action() def some_action(amount, force=”False”):
“Usage: some-action AMOUNT [force=True]” # <– shown on error # process the arguments # do some calls # return extra-vars to be passed to ansible-playbook return {
‘amount’: int(amount), ‘type’: force,}
You can now create a symlink to hooks.py that can be invoked like a hook, but with cli params:
# link actions/some-action to hooks/hooks.py
actions/some-action amount=10 force=true
-
class
charmhelpers.contrib.ansible.
AnsibleHooks
(playbook_path, default_hooks=None)¶ Bases:
charmhelpers.core.hookenv.Hooks
Run a playbook with the hook-name as the tag.
This helper builds on the standard hookenv.Hooks helper, but additionally runs the playbook with the hook-name specified using –tags (ie. running all the tasks tagged with the hook-name).
Example:
hooks = AnsibleHooks(playbook_path='playbooks/my_machine_state.yaml') # All the tasks within my_machine_state.yaml tagged with 'install' # will be run automatically after do_custom_work() @hooks.hook() def install(): do_custom_work() # For most of your hooks, you won't need to do anything other # than run the tagged tasks for the hook: @hooks.hook('config-changed', 'start', 'stop') def just_use_playbook(): pass # As a convenience, you can avoid the above noop function by specifying # the hooks which are handled by ansible-only and they'll be registered # for you: # hooks = AnsibleHooks( # 'playbooks/my_machine_state.yaml', # default_hooks=['config-changed', 'start', 'stop']) if __name__ == "__main__": # execute a hook based on the name the program is called by hooks.execute(sys.argv)
-
action
(*action_names)¶ Decorator, registering them as actions
-
execute
(args)¶ Execute the hook followed by the playbook using the hook as tag.
-
register_action
(name, function)¶ Register a hook
-
-
charmhelpers.contrib.ansible.
apply_playbook
(playbook, tags=None, extra_vars=None)¶
-
charmhelpers.contrib.ansible.
install_ansible_support
(from_ppa=True, ppa_location='ppa:rquillo/ansible')¶ Installs the ansible package.
By default it is installed from the PPA linked from the ansible website or from a ppa specified by a charm config..
If from_ppa is empty, you must ensure that the package is available from a configured repository.
charmhelpers.contrib.charmhelpers package¶
-
charmhelpers.contrib.charmhelpers.
unit_info
(service_name, item_name, data=None, unit=None)¶
-
charmhelpers.contrib.charmhelpers.
wait_for_machine
(num_machines=1, timeout=300)¶ Wait timeout seconds for num_machines machines to come up.
This wait_for... function can be called by other wait_for functions whose timeouts might be too short in situations where only a bare Juju setup has been bootstrapped.
Returns: A tuple of (num_machines, time_taken). This is used for testing.
-
charmhelpers.contrib.charmhelpers.
wait_for_page_contents
(url, contents, timeout=120, validate=None)¶
-
charmhelpers.contrib.charmhelpers.
wait_for_relation
(service_name, relation_name, timeout=120)¶ Wait timeout seconds for a given relation to come up.
-
charmhelpers.contrib.charmhelpers.
wait_for_unit
(service_name, timeout=480)¶ Wait timeout seconds for a given service name to come up.
charmhelpers.contrib.charmsupport package¶
charmhelpers.contrib.charmsupport.nrpe module¶
Compatibility with the nrpe-external-master charm
-
class
charmhelpers.contrib.charmsupport.nrpe.
Check
(shortname, description, check_cmd)¶ Bases:
object
-
run
()¶
-
service_template
= '\n#---------------------------------------------------\n# This file is Juju managed\n#---------------------------------------------------\ndefine service {{\n use active-service\n host_name {nagios_hostname}\n service_description {nagios_hostname}[{shortname}] {description}\n check_command check_nrpe!{command}\n servicegroups {nagios_servicegroup}\n}}\n'¶
-
shortname_re
= '[A-Za-z0-9-_]+$'¶
-
write
(nagios_context, hostname, nagios_servicegroups)¶
-
write_service_config
(nagios_context, hostname, nagios_servicegroups)¶
-
-
exception
charmhelpers.contrib.charmsupport.nrpe.
CheckException
¶ Bases:
exceptions.Exception
-
class
charmhelpers.contrib.charmsupport.nrpe.
NRPE
(hostname=None)¶ Bases:
object
-
add_check
(*args, **kwargs)¶
-
nagios_exportdir
= '/var/lib/nagios/export'¶
-
nagios_logdir
= '/var/log/nagios'¶
-
nrpe_confdir
= '/etc/nagios/nrpe.d'¶
-
write
()¶
-
-
charmhelpers.contrib.charmsupport.nrpe.
add_haproxy_checks
(nrpe, unit_name)¶ Add checks for each service in list
Parameters: - nrpe (NRPE) – NRPE object to add check to
- unit_name (str) – Unit name to use in check description
-
charmhelpers.contrib.charmsupport.nrpe.
add_init_service_checks
(nrpe, services, unit_name)¶ Add checks for each service in list
Parameters: - nrpe (NRPE) – NRPE object to add check to
- services (list) – List of services to check
- unit_name (str) – Unit name to use in check description
-
charmhelpers.contrib.charmsupport.nrpe.
copy_nrpe_checks
()¶ Copy the nrpe checks into place
-
charmhelpers.contrib.charmsupport.nrpe.
get_nagios_hostcontext
(relation_name='nrpe-external-master')¶ Query relation with nrpe subordinate, return the nagios_host_context
Parameters: relation_name (str) – Name of relation nrpe sub joined to
-
charmhelpers.contrib.charmsupport.nrpe.
get_nagios_hostname
(relation_name='nrpe-external-master')¶ Query relation with nrpe subordinate, return the nagios_hostname
Parameters: relation_name (str) – Name of relation nrpe sub joined to
-
charmhelpers.contrib.charmsupport.nrpe.
get_nagios_unit_name
(relation_name='nrpe-external-master')¶ Return the nagios unit name prepended with host_context if needed
Parameters: relation_name (str) – Name of relation nrpe sub joined to
charmhelpers.contrib.charmsupport.volumes module¶
Functions for managing volumes in juju units. One volume is supported per unit. Subordinates may have their own storage, provided it is on its own partition.
Configuration stanzas:
volume-ephemeral:
type: boolean
default: true
description: >
If false, a volume is mounted as sepecified in "volume-map"
If true, ephemeral storage will be used, meaning that log data
will only exist as long as the machine. YOU HAVE BEEN WARNED.
volume-map:
type: string
default: {}
description: >
YAML map of units to device names, e.g:
"{ rsyslog/0: /dev/vdb, rsyslog/1: /dev/vdb }"
Service units will raise a configure-error if volume-ephemeral
is 'true' and no volume-map value is set. Use 'juju set' to set a
value and 'juju resolved' to complete configuration.
Usage:
from charmsupport.volumes import configure_volume, VolumeConfigurationError
from charmsupport.hookenv import log, ERROR
def post_mount_hook():
stop_service('myservice')
def post_mount_hook():
start_service('myservice')
if __name__ == '__main__':
try:
configure_volume(before_change=pre_mount_hook,
after_change=post_mount_hook)
except VolumeConfigurationError:
log('Storage could not be configured', ERROR)
-
exception
charmhelpers.contrib.charmsupport.volumes.
VolumeConfigurationError
¶ Bases:
exceptions.Exception
Volume configuration data is missing or invalid
-
charmhelpers.contrib.charmsupport.volumes.
configure_volume
(before_change=<function <lambda>>, after_change=<function <lambda>>)¶ Set up storage (or don’t) according to the charm’s volume configuration. Returns the mount point or “ephemeral”. before_change and after_change are optional functions to be called if the volume configuration changes.
-
charmhelpers.contrib.charmsupport.volumes.
get_config
()¶ Gather and sanity-check volume configuration data
-
charmhelpers.contrib.charmsupport.volumes.
managed_mounts
()¶ List of all mounted managed volumes
-
charmhelpers.contrib.charmsupport.volumes.
mount_volume
(config)¶
-
charmhelpers.contrib.charmsupport.volumes.
unmount_volume
(config)¶
charmhelpers.contrib.hahelpers package¶
charmhelpers.contrib.hahelpers.apache module¶
-
charmhelpers.contrib.hahelpers.apache.
get_ca_cert
()¶
-
charmhelpers.contrib.hahelpers.apache.
get_cert
(cn=None)¶
-
charmhelpers.contrib.hahelpers.apache.
install_ca_cert
(ca_cert)¶
charmhelpers.contrib.hahelpers.cluster module¶
Helpers for clustering and determining “cluster leadership” and other clustering-related helpers.
-
exception
charmhelpers.contrib.hahelpers.cluster.
CRMDCNotFound
¶ Bases:
exceptions.Exception
-
exception
charmhelpers.contrib.hahelpers.cluster.
CRMResourceNotFound
¶ Bases:
exceptions.Exception
-
exception
charmhelpers.contrib.hahelpers.cluster.
HAIncompleteConfig
¶ Bases:
exceptions.Exception
-
charmhelpers.contrib.hahelpers.cluster.
canonical_url
(configs, vip_setting='vip')¶ Returns the correct HTTP URL to this host given the state of HTTPS configuration and hacluster.
- :configs : OSTemplateRenderer: A config tempating object to inspect for
- a complete https context.
Vip_setting: str: Setting in charm config that specifies VIP address.
-
charmhelpers.contrib.hahelpers.cluster.
determine_apache_port
(public_port, singlenode_mode=False)¶ Description: Determine correct apache listening port based on public IP + state of the cluster.
public_port: int: standard public port for given service
singlenode_mode: boolean: Shuffle ports when only a single unit is present
returns: int: the correct listening port for the HAProxy service
-
charmhelpers.contrib.hahelpers.cluster.
determine_api_port
(public_port, singlenode_mode=False)¶ Determine correct API server listening port based on existence of HTTPS reverse proxy and/or haproxy.
public_port: int: standard public port for given service
singlenode_mode: boolean: Shuffle ports when only a single unit is present
returns: int: the correct listening port for the API service
-
charmhelpers.contrib.hahelpers.cluster.
eligible_leader
(resource)¶
-
charmhelpers.contrib.hahelpers.cluster.
get_hacluster_config
(exclude_keys=None)¶ Obtains all relevant configuration from charm configuration required for initiating a relation to hacluster:
ha-bindiface, ha-mcastport, vipparam: exclude_keys: list of setting key(s) to be excluded. returns: dict: A dict containing settings keyed by setting name. raises: HAIncompleteConfig if settings are missing.
-
charmhelpers.contrib.hahelpers.cluster.
https
()¶ Determines whether enough data has been provided in configuration or relation data to configure HTTPS . returns: boolean
-
charmhelpers.contrib.hahelpers.cluster.
is_clustered
()¶
-
charmhelpers.contrib.hahelpers.cluster.
is_crm_dc
()¶ Determine leadership by querying the pacemaker Designated Controller
-
charmhelpers.contrib.hahelpers.cluster.
is_elected_leader
(resource)¶ Returns True if the charm executing this is the elected cluster leader.
- It relies on two mechanisms to determine leadership:
- 1. If juju is sufficiently new and leadership election is supported, the is_leader command will be used. 2. If the charm is part of a corosync cluster, call corosync to determine leadership. 3. If the charm is not part of a corosync cluster, the leader is determined as being “the alive unit with the lowest unit numer”. In other words, the oldest surviving unit.
-
charmhelpers.contrib.hahelpers.cluster.
is_leader
(resource)¶
-
charmhelpers.contrib.hahelpers.cluster.
oldest_peer
(peers)¶ Determines who the oldest peer is by comparing unit numbers.
-
charmhelpers.contrib.hahelpers.cluster.
peer_ips
(peer_relation='cluster', addr_key='private-address')¶ Return a dict of peers and their private-address
-
charmhelpers.contrib.hahelpers.cluster.
peer_units
(peer_relation='cluster')¶
charmhelpers.contrib.network package¶
charmhelpers.contrib.network.ovs package¶
Helpers for interacting with OpenvSwitch
-
charmhelpers.contrib.network.ovs.
add_bridge
(name)¶ Add the named bridge to openvswitch
-
charmhelpers.contrib.network.ovs.
add_bridge_port
(name, port, promisc=False)¶ Add a port to the named openvswitch bridge
-
charmhelpers.contrib.network.ovs.
del_bridge
(name)¶ Delete the named bridge from openvswitch
-
charmhelpers.contrib.network.ovs.
del_bridge_port
(name, port)¶ Delete a port from the named openvswitch bridge
-
charmhelpers.contrib.network.ovs.
full_restart
()¶ Full restart and reload of openvswitch
-
charmhelpers.contrib.network.ovs.
get_certificate
()¶ Read openvswitch certificate from disk
-
charmhelpers.contrib.network.ovs.
set_manager
(manager)¶ Set the controller for the local openvswitch
charmhelpers.contrib.network.ip module¶
-
charmhelpers.contrib.network.ip.
format_ipv6_addr
(address)¶ If address is IPv6, wrap it in ‘[]’ otherwise return None.
This is required by most configuration files when specifying IPv6 addresses.
-
charmhelpers.contrib.network.ip.
get_address_in_network
(network, fallback=None, fatal=False)¶ Get an IPv4 or IPv6 address within the network from the host.
Parameters: - (str) (fallback) – CIDR presentation format. For example, ‘192.168.1.0/24’.
- (str) – If no address is found, return fallback.
- (boolean) (fatal) – If no address is found, fallback is not set and fatal is True then exit(1).
-
charmhelpers.contrib.network.ip.
get_bridge_nics
(bridge, vnic_dir='/sys/devices/virtual/net')¶ Return a list of nics comprising a given bridge on the system.
-
charmhelpers.contrib.network.ip.
get_bridges
(vnic_dir='/sys/devices/virtual/net')¶ Return a list of bridges on the system.
-
charmhelpers.contrib.network.ip.
get_host_ip
(hostname, fallback=None)¶ Resolves the IP for a given hostname, or returns the input if it is already an IP.
-
charmhelpers.contrib.network.ip.
get_hostname
(address, fqdn=True)¶ Resolves hostname for given IP, or returns the input if it is already a hostname.
-
charmhelpers.contrib.network.ip.
get_iface_addr
(iface='eth0', inet_type='AF_INET', inc_aliases=False, fatal=True, exc_list=None)¶ Return the assigned IP address for a given interface, if any.
-
charmhelpers.contrib.network.ip.
get_iface_from_addr
(addr)¶ Work out on which interface the provided address is configured.
-
charmhelpers.contrib.network.ip.
get_ipv6_addr
(*args, **kwargs)¶
-
charmhelpers.contrib.network.ip.
is_address_in_network
(network, address)¶ Determine whether the provided address is within a network range.
Parameters: - (str) (network) – CIDR presentation format. For example, ‘192.168.1.0/24’.
- address – An individual IPv4 or IPv6 address without a net mask or subnet prefix. For example, ‘192.168.1.1’.
Returns boolean: Flag indicating whether address is in network.
-
charmhelpers.contrib.network.ip.
is_bridge_member
(nic)¶ Check if a given nic is a member of a bridge.
-
charmhelpers.contrib.network.ip.
is_ip
(address)¶ Returns True if address is a valid IP address.
-
charmhelpers.contrib.network.ip.
is_ipv6
(address)¶ Determine whether provided address is IPv6 or not.
-
charmhelpers.contrib.network.ip.
no_ip_found_error_out
(network)¶
-
charmhelpers.contrib.network.ip.
ns_query
(address)¶
-
charmhelpers.contrib.network.ip.
sniff_iface
(f)¶ Ensure decorated function is called with a value for iface.
If no iface provided, inject net iface inferred from unit private address.
charmhelpers.contrib.openstack package¶
charmhelpers.contrib.openstack.templates package¶
charmhelpers.contrib.openstack.alternatives module¶
Helper for managing alternatives for file conflict resolution
-
charmhelpers.contrib.openstack.alternatives.
install_alternative
(name, target, source, priority=50)¶ Install alternative configuration
charmhelpers.contrib.openstack.context module¶
-
class
charmhelpers.contrib.openstack.context.
AMQPContext
(ssl_dir=None, rel_name='amqp', relation_prefix=None)¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
class
charmhelpers.contrib.openstack.context.
ApacheSSLContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
Generates a context for an apache vhost configuration that configures HTTPS reverse proxying for one or many endpoints. Generated context looks something like:
{ 'namespace': 'cinder', 'private_address': 'iscsi.mycinderhost.com', 'endpoints': [(8776, 8766), (8777, 8767)] }
The endpoints list consists of a tuples mapping external ports to internal ports.
-
canonical_names
()¶ Figure out which canonical names clients will access this service.
-
configure_ca
()¶
-
configure_cert
(cn=None)¶
-
enable_modules
()¶
-
external_ports
= []¶
-
get_network_addresses
()¶ - For each network configured, return corresponding address and vip
- (if available).
Returns a list of tuples of the form:
- [(address_in_net_a, vip_in_net_a),
- (address_in_net_b, vip_in_net_b), ...]
or, if no vip(s) available:
- [(address_in_net_a, address_in_net_a),
- (address_in_net_b, address_in_net_b), ...]
-
interfaces
= ['https']¶
-
service_namespace
= None¶
-
-
class
charmhelpers.contrib.openstack.context.
BindHostContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
class
charmhelpers.contrib.openstack.context.
CephContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
Generates context for /etc/ceph/ceph.conf templates.
-
interfaces
= ['ceph']¶
-
-
class
charmhelpers.contrib.openstack.context.
DataPortContext
¶ Bases:
charmhelpers.contrib.openstack.context.NeutronPortContext
-
class
charmhelpers.contrib.openstack.context.
ExternalPortContext
¶ Bases:
charmhelpers.contrib.openstack.context.NeutronPortContext
-
class
charmhelpers.contrib.openstack.context.
HAProxyContext
(singlenode_mode=False)¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
Provides half a context for the haproxy template, which describes all peers to be included in the cluster. Each charm needs to include its own context generator that describes the port mapping.
-
interfaces
= ['cluster']¶
-
-
class
charmhelpers.contrib.openstack.context.
IdentityServiceContext
(service=None, service_user=None, rel_name='identity-service')¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
class
charmhelpers.contrib.openstack.context.
ImageServiceContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
interfaces
= ['image-service']¶
-
-
class
charmhelpers.contrib.openstack.context.
LogLevelContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
class
charmhelpers.contrib.openstack.context.
NetworkServiceContext
(rel_name='quantum-network-service')¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
class
charmhelpers.contrib.openstack.context.
NeutronAPIContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
Inspects current neutron-plugin-api relation for neutron settings. Return defaults if it is not present.
-
get_neutron_options
(rdata)¶
-
interfaces
= ['neutron-plugin-api']¶
-
-
class
charmhelpers.contrib.openstack.context.
NeutronContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
calico_ctxt
()¶
-
interfaces
= []¶
-
n1kv_ctxt
()¶
-
network_manager
¶
-
neutron_ctxt
()¶
-
neutron_security_groups
¶
-
nuage_ctxt
()¶
-
nvp_ctxt
()¶
-
ovs_ctxt
()¶
-
packages
¶
-
plugin
¶
-
-
class
charmhelpers.contrib.openstack.context.
NeutronPortContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
NIC_PREFIXES
= ['eth', 'bond']¶
-
resolve_ports
(ports)¶ Resolve NICs not yet bound to bridge(s)
If hwaddress provided then returns resolved hwaddress otherwise NIC.
-
-
class
charmhelpers.contrib.openstack.context.
NotificationDriverContext
(zmq_relation='zeromq-configuration', amqp_relation='amqp')¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
class
charmhelpers.contrib.openstack.context.
OSConfigFlagContext
(charm_flag='config-flags', template_flag='user_config_flags')¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
Provides support for user-defined config flags.
Users can define a comma-seperated list of key=value pairs in the charm configuration and apply them at any point in any file by using a template flag.
Sometimes users might want config flags inserted within a specific section so this class allows users to specify the template flag name, allowing for multiple template flags (sections) within the same context.
- NOTE: the value of config-flags may be a comma-separated list of
- key=value pairs and some Openstack config files support comma-separated lists as values.
-
exception
charmhelpers.contrib.openstack.context.
OSContextError
¶ Bases:
exceptions.Exception
-
class
charmhelpers.contrib.openstack.context.
OSContextGenerator
¶ Bases:
object
Base class for all context generators.
-
interfaces
= []¶
-
-
class
charmhelpers.contrib.openstack.context.
PhyNICMTUContext
¶ Bases:
charmhelpers.contrib.openstack.context.DataPortContext
-
class
charmhelpers.contrib.openstack.context.
PostgresqlDBContext
(database=None)¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
interfaces
= ['pgsql-db']¶
-
-
class
charmhelpers.contrib.openstack.context.
SubordinateConfigContext
(service, config_file, interface)¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
Responsible for inspecting relations to subordinates that may be exporting required config via a json blob.
The subordinate interface allows subordinates to export their configuration requirements to the principle for multiple config files and multiple serivces. Ie, a subordinate that has interfaces to both glance and nova may export to following yaml blob as json:
glance: /etc/glance/glance-api.conf: sections: DEFAULT: - [key1, value1] /etc/glance/glance-registry.conf: MYSECTION: - [key2, value2] nova: /etc/nova/nova.conf: sections: DEFAULT: - [key3, value3]
It is then up to the principle charms to subscribe this context to the service+config file it is interestd in. Configuration data will be available in the template context, in glance’s case, as:
ctxt = { ... other context ... 'subordinate_config': { 'DEFAULT': { 'key1': 'value1', }, 'MYSECTION': { 'key2': 'value2', }, } }
-
class
charmhelpers.contrib.openstack.context.
SysctlContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
This context check if the ‘sysctl’ option exists on configuration then creates a file with the loaded contents
-
class
charmhelpers.contrib.openstack.context.
SyslogContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
class
charmhelpers.contrib.openstack.context.
WorkerConfigContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
num_cpus
¶
-
-
class
charmhelpers.contrib.openstack.context.
ZeroMQContext
¶ Bases:
charmhelpers.contrib.openstack.context.OSContextGenerator
-
interfaces
= ['zeromq-configuration']¶
-
-
charmhelpers.contrib.openstack.context.
config_flags_parser
(config_flags)¶ Parses config flags string into dict.
This parsing method supports a few different formats for the config flag values to be parsed:
A string in the simple format of key=value pairs, with the possibility of specifying multiple key value pairs within the same string. For example, a string in the format of ‘key1=value1, key2=value2’ will return a dict of:
- {‘key1’: ‘value1’,
‘key2’: ‘value2’}.
A string in the above format, but supporting a comma-delimited list of values for the same key. For example, a string in the format of ‘key1=value1, key2=value3,value4,value5’ will return a dict of:
- {‘key1’, ‘value1’,
‘key2’, ‘value2,value3,value4’}
A string containing a colon character (:) prior to an equal character (=) will be treated as yaml and parsed as such. This can be used to specify more complex key value pairs. For example, a string in the format of ‘key1: subkey1=value1, subkey2=value2’ will return a dict of:
{‘key1’, ‘subkey1=value1, subkey2=value2’}
The provided config_flags string may be a list of comma-separated values which themselves may be comma-separated list of values.
-
charmhelpers.contrib.openstack.context.
context_complete
(ctxt)¶
-
charmhelpers.contrib.openstack.context.
db_ssl
(rdata, ctxt, ssl_dir)¶
-
charmhelpers.contrib.openstack.context.
ensure_packages
(packages)¶ Install but do not upgrade required plugin packages.
charmhelpers.contrib.openstack.neutron module¶
-
charmhelpers.contrib.openstack.neutron.
determine_dkms_package
()¶ Determine which DKMS package should be used based on kernel version
-
charmhelpers.contrib.openstack.neutron.
headers_package
()¶ Ensures correct linux-headers for running kernel are installed, for building DKMS package
-
charmhelpers.contrib.openstack.neutron.
kernel_version
()¶ Retrieve the current major kernel version as a tuple e.g. (3, 13)
-
charmhelpers.contrib.openstack.neutron.
network_manager
()¶ Deals with the renaming of Quantum to Neutron in H and any situations that require compatability (eg, deploying H with network-manager=quantum, upgrading from G).
-
charmhelpers.contrib.openstack.neutron.
neutron_plugin_attribute
(plugin, attr, net_manager=None)¶
-
charmhelpers.contrib.openstack.neutron.
neutron_plugins
()¶
-
charmhelpers.contrib.openstack.neutron.
parse_bridge_mappings
(mappings)¶ Parse bridge mappings.
Mappings must be a space-delimited list of provider:bridge mappings.
Returns dict of the form {provider:bridge}.
-
charmhelpers.contrib.openstack.neutron.
parse_data_port_mappings
(mappings, default_bridge='br-data')¶ Parse data port mappings.
Mappings must be a space-delimited list of bridge:port mappings.
Returns dict of the form {bridge:port}.
-
charmhelpers.contrib.openstack.neutron.
parse_mappings
(mappings)¶
-
charmhelpers.contrib.openstack.neutron.
parse_vlan_range_mappings
(mappings)¶ Parse vlan range mappings.
Mappings must be a space-delimited list of provider:start:end mappings.
The start:end range is optional and may be omitted.
Returns dict of the form {provider: (start, end)}.
-
charmhelpers.contrib.openstack.neutron.
quantum_plugins
()¶
charmhelpers.contrib.openstack.templating module¶
-
exception
charmhelpers.contrib.openstack.templating.
OSConfigException
¶ Bases:
exceptions.Exception
-
class
charmhelpers.contrib.openstack.templating.
OSConfigRenderer
(templates_dir, openstack_release)¶ Bases:
object
This class provides a common templating system to be used by OpenStack charms. It is intended to help charms share common code and templates, and ease the burden of managing config templates across multiple OpenStack releases.
Basic usage:
# import some common context generates from charmhelpers from charmhelpers.contrib.openstack import context # Create a renderer object for a specific OS release. configs = OSConfigRenderer(templates_dir='/tmp/templates', openstack_release='folsom') # register some config files with context generators. configs.register(config_file='/etc/nova/nova.conf', contexts=[context.SharedDBContext(), context.AMQPContext()]) configs.register(config_file='/etc/nova/api-paste.ini', contexts=[context.IdentityServiceContext()]) configs.register(config_file='/etc/haproxy/haproxy.conf', contexts=[context.HAProxyContext()]) # write out a single config configs.write('/etc/nova/nova.conf') # write out all registered configs configs.write_all()
OpenStack Releases and template loading
When the object is instantiated, it is associated with a specific OS release. This dictates how the template loader will be constructed.
The constructed loader attempts to load the template from several places in the following order: - from the most recent OS release-specific template dir (if one exists) - the base templates_dir - a template directory shipped in the charm with this helper file.
For the example above, ‘/tmp/templates’ contains the following structure:
/tmp/templates/nova.conf /tmp/templates/api-paste.ini /tmp/templates/grizzly/api-paste.ini /tmp/templates/havana/api-paste.ini
Since it was registered with the grizzly release, it first seraches the grizzly directory for nova.conf, then the templates dir.
When writing api-paste.ini, it will find the template in the grizzly directory.
If the object were created with folsom, it would fall back to the base templates dir for its api-paste.ini template.
This system should help manage changes in config files through openstack releases, allowing charms to fall back to the most recently updated config template for a given release
The haproxy.conf, since it is not shipped in the templates dir, will be loaded from the module directory’s template directory, eg $CHARM/hooks/charmhelpers/contrib/openstack/templates. This allows us to ship common templates (haproxy, apache) with the helpers.
Context generators
Context generators are used to generate template contexts during hook execution. Doing so may require inspecting service relations, charm config, etc. When registered, a config file is associated with a list of generators. When a template is rendered and written, all context generates are called in a chain to generate the context dictionary passed to the jinja2 template. See context.py for more info.
-
complete_contexts
()¶ Returns a list of context interfaces that yield a complete context.
-
register
(config_file, contexts)¶ Register a config file with a list of context generators to be called during rendering.
-
render
(config_file)¶
-
set_release
(openstack_release)¶ Resets the template environment and generates a new template loader based on a the new openstack release.
-
write
(config_file)¶ Write a single config file, raises if config file is not registered.
-
write_all
()¶ Write out all registered config files.
-
-
class
charmhelpers.contrib.openstack.templating.
OSConfigTemplate
(config_file, contexts)¶ Bases:
object
Associates a config file template with a list of context generators. Responsible for constructing a template context based on those generators.
-
complete_contexts
()¶ Return a list of interfaces that have atisfied contexts.
-
context
()¶
-
-
charmhelpers.contrib.openstack.templating.
get_loader
(templates_dir, os_release)¶ Create a jinja2.ChoiceLoader containing template dirs up to and including os_release. If directory template directory is missing at templates_dir, it will be omitted from the loader. templates_dir is added to the bottom of the search list as a base loading dir.
A charm may also ship a templates dir with this module and it will be appended to the bottom of the search list, eg:
hooks/charmhelpers/contrib/openstack/templates
Parameters: - (str) (os_release) – Base template directory containing release sub-directories.
- (str) – OpenStack release codename to construct template loader.
Returns: jinja2.ChoiceLoader constructed with a list of jinja2.FilesystemLoaders, ordered in descending order by OpenStack release.
charmhelpers.contrib.openstack.utils module¶
-
charmhelpers.contrib.openstack.utils.
clean_storage
(block_device)¶ - Ensures a block device is clean. That is:
- unmounted
- any lvm volume groups are deactivated
- any lvm physical device signatures removed
- partition table wiped
Parameters: block_device – str: Full path to block device to clean.
-
charmhelpers.contrib.openstack.utils.
config_value_changed
(option)¶ Determine if config value changed since last call to this function.
-
charmhelpers.contrib.openstack.utils.
configure_installation_source
(rel)¶ Configure apt installation source.
-
charmhelpers.contrib.openstack.utils.
ensure_block_device
(block_device)¶ Confirm block_device, create as loopback if necessary.
Parameters: block_device – str: Full path of block device to ensure. Returns: str: Full path of ensured block device.
-
charmhelpers.contrib.openstack.utils.
error_out
(msg)¶
-
charmhelpers.contrib.openstack.utils.
get_matchmaker_map
(mm_file='/etc/oslo/matchmaker_ring.json')¶
-
charmhelpers.contrib.openstack.utils.
get_os_codename_install_source
(src)¶ Derive OpenStack release codename from a given installation source.
-
charmhelpers.contrib.openstack.utils.
get_os_codename_package
(package, fatal=True)¶ Derive OpenStack release codename from an installed package.
-
charmhelpers.contrib.openstack.utils.
get_os_codename_version
(vers)¶ Determine OpenStack codename from version number.
-
charmhelpers.contrib.openstack.utils.
get_os_version_codename
(codename)¶ Determine OpenStack version number from codename.
-
charmhelpers.contrib.openstack.utils.
get_os_version_install_source
(src)¶
-
charmhelpers.contrib.openstack.utils.
get_os_version_package
(pkg, fatal=True)¶ Derive OpenStack version number from an installed package.
-
charmhelpers.contrib.openstack.utils.
git_clone_and_install
(projects_yaml, core_project, depth=1)¶ Clone/install all specified OpenStack repositories.
The expected format of projects_yaml is:
- repositories:
- {name: keystone,
- repository: ‘git://git.openstack.org/openstack/keystone.git’, branch: ‘stable/icehouse’}
- {name: requirements,
- repository: ‘git://git.openstack.org/openstack/requirements.git’, branch: ‘stable/icehouse’}
directory: /mnt/openstack-git http_proxy: squid-proxy-url https_proxy: squid-proxy-url
The directory, http_proxy, and https_proxy keys are optional.
-
charmhelpers.contrib.openstack.utils.
git_install_requested
()¶ Returns true if openstack-origin-git is specified.
-
charmhelpers.contrib.openstack.utils.
git_pip_venv_dir
(projects_yaml)¶ Return the pip virtualenv path.
-
charmhelpers.contrib.openstack.utils.
git_src_dir
(projects_yaml, project)¶ Return the directory where the specified project’s source is located.
-
charmhelpers.contrib.openstack.utils.
git_yaml_value
(projects_yaml, key)¶ Return the value in projects_yaml for the specified key.
-
charmhelpers.contrib.openstack.utils.
import_key
(keyid)¶
-
charmhelpers.contrib.openstack.utils.
openstack_upgrade_available
(package)¶ Determines if an OpenStack upgrade is available from installation source, based on version of installed package.
Parameters: package – str: Name of installed package. Returns: bool: : Returns True if configured installation source offers a newer version of package.
-
charmhelpers.contrib.openstack.utils.
os_release
(package, base='essex')¶ Returns OpenStack release codename from a cached global. If the codename can not be determined from either an installed package or the installation source, the earliest release supported by the charm should be returned.
-
charmhelpers.contrib.openstack.utils.
os_requires_version
(ostack_release, pkg)¶ Decorator for hook to specify minimum supported release
-
charmhelpers.contrib.openstack.utils.
save_script_rc
(script_path='scripts/scriptrc', **env_vars)¶ Write an rc file in the charm-delivered directory containing exported environment variables provided by env_vars. Any charm scripts run outside the juju hook environment can source this scriptrc to obtain updated config information necessary to perform health checks or service changes.
-
charmhelpers.contrib.openstack.utils.
sync_db_with_multi_ipv6_addresses
(database, database_user, relation_prefix=None)¶
charmhelpers.contrib.peerstorage package¶
-
charmhelpers.contrib.peerstorage.
leader_get
(attribute=None)¶ Wrapper to ensure that settings are migrated from the peer relation.
This is to support upgrading an environment that does not support Juju leadership election to one that does.
If a setting is not extant in the leader-get but is on the relation-get peer rel, it is migrated and marked as such so that it is not re-migrated.
-
charmhelpers.contrib.peerstorage.
peer_echo
(includes=None, force=False)¶ Echo filtered attributes back onto the same relation for storage.
This is a requirement to use the peerstorage module - it needs to be called from the peer relation’s changed hook.
If Juju leader support exists this will be a noop unless force is True.
-
charmhelpers.contrib.peerstorage.
peer_retrieve
(key, relation_name='cluster')¶ Retrieve a named key from peer relation relation_name.
-
charmhelpers.contrib.peerstorage.
peer_retrieve_by_prefix
(prefix, relation_name='cluster', delimiter='_', inc_list=None, exc_list=None)¶ Retrieve k/v pairs given a prefix and filter using {inc,exc}_list
-
charmhelpers.contrib.peerstorage.
peer_store
(key, value, relation_name='cluster')¶ Store the key/value pair on the named peer relation relation_name.
-
charmhelpers.contrib.peerstorage.
peer_store_and_set
(relation_id=None, peer_relation_name='cluster', peer_store_fatal=False, relation_settings=None, delimiter='_', **kwargs)¶ Store passed-in arguments both in argument relation and in peer storage.
It functions like doing relation_set() and peer_store() at the same time, with the same data.
- @param relation_id: the id of the relation to store the data on. Defaults
- to the current relation.
- @param peer_store_fatal: Set to True, the function will raise an exception
- should the peer sotrage not be avialable.
-
charmhelpers.contrib.peerstorage.
relation_get
(attribute=None, unit=None, rid=None)¶ Attempt to use leader-get if supported in the current version of Juju, otherwise falls back on relation-get.
Note that we only attempt to use leader-get if the provided rid is a peer relation id or no relation id is provided (in which case we assume we are within the peer relation context).
-
charmhelpers.contrib.peerstorage.
relation_set
(relation_id=None, relation_settings=None, **kwargs)¶ Attempt to use leader-set if supported in the current version of Juju, otherwise falls back on relation-set.
Note that we only attempt to use leader-set if the provided relation_id is a peer relation id or no relation id is provided (in which case we assume we are within the peer relation context).
charmhelpers.contrib.python package¶
charmhelpers.contrib.python.debug module¶
-
charmhelpers.contrib.python.debug.
set_trace
(addr='0.0.0.0', port=4444)¶ Set a trace point using the remote debugger
charmhelpers.contrib.python.packages module¶
-
charmhelpers.contrib.python.packages.
parse_options
(given, available)¶ Given a set of options, check if available
-
charmhelpers.contrib.python.packages.
pip_create_virtualenv
(path=None)¶ Create an isolated Python environment.
-
charmhelpers.contrib.python.packages.
pip_install
(package, fatal=False, upgrade=False, venv=None, **options)¶ Install a python package
-
charmhelpers.contrib.python.packages.
pip_install_requirements
(requirements, **options)¶ Install a requirements file
-
charmhelpers.contrib.python.packages.
pip_list
()¶ Returns the list of current python installed packages
-
charmhelpers.contrib.python.packages.
pip_uninstall
(package, **options)¶ Uninstall a python package
charmhelpers.contrib.python.rpdb module¶
Remote Python Debugger (pdb wrapper).
-
class
charmhelpers.contrib.python.rpdb.
Rpdb
(addr='127.0.0.1', port=4444)¶ Bases:
pdb.Pdb
-
do_EOF
(arg)¶ Stop all operation on
continue
.
-
do_c
(arg)¶ Stop all operation on
continue
.
-
do_cont
(arg)¶ Stop all operation on
continue
.
-
do_continue
(arg)¶ Stop all operation on
continue
.
-
do_exit
(arg)¶ Stop all operation on
continue
.
-
do_quit
(arg)¶ Stop all operation on
continue
.
-
shutdown
()¶ Revert stdin and stdout, close the socket.
-
charmhelpers.contrib.saltstack package¶
Charm Helpers saltstack - declare the state of your machines.
This helper enables you to declare your machine state, rather than program it procedurally (and have to test each change to your procedures). Your install hook can be as simple as:
{{{
from charmhelpers.contrib.saltstack import (
install_salt_support,
update_machine_state,
)
def install():
install_salt_support()
update_machine_state('machine_states/dependencies.yaml')
update_machine_state('machine_states/installed.yaml')
}}}
and won’t need to change (nor will its tests) when you change the machine state.
It’s using a python package called salt-minion which allows various formats for specifying resources, such as:
{{{
/srv/{{ basedir }}:
file.directory:
- group: ubunet
- user: ubunet
- require:
- user: ubunet
- recurse:
- user
- group
ubunet:
group.present:
- gid: 1500
user.present:
- uid: 1500
- gid: 1500
- createhome: False
- require:
- group: ubunet
}}}
- The docs for all the different state definitions are at:
- http://docs.saltstack.com/ref/states/all/
- TODO:
- Add test helpers which will ensure that machine state definitions are functionally (but not necessarily logically) correct (ie. getting salt to parse all state defs.
- Add a link to a public bootstrap charm example / blogpost.
- Find a way to obviate the need to use the grains[‘charm_dir’] syntax in templates.
-
charmhelpers.contrib.saltstack.
install_salt_support
(from_ppa=True)¶ Installs the salt-minion helper for machine state.
By default the salt-minion package is installed from the saltstack PPA. If from_ppa is False you must ensure that the salt-minion package is available in the apt cache.
-
charmhelpers.contrib.saltstack.
update_machine_state
(state_path)¶ Update the machine state using the provided state declaration.
charmhelpers.contrib.ssl package¶
charmhelpers.contrib.ssl.service module¶
-
class
charmhelpers.contrib.ssl.service.
ServiceCA
(name, ca_dir, cert_type='standard')¶ Bases:
object
-
ca_cert
¶
-
ca_conf
¶
-
ca_key
¶
-
create_certificate
(common_name)¶
-
default_ca_expiry
= '2190'¶
-
default_expiry
= '730'¶
-
static
get_ca
(type='standard')¶
-
get_ca_bundle
()¶
-
get_certificate
(common_name)¶
-
get_conf_variables
()¶
-
get_or_create_cert
(common_name)¶
-
classmethod
get_service_cert
(type='standard')¶
-
init
()¶
-
signing_conf
¶
-
-
charmhelpers.contrib.ssl.
generate_selfsigned
(keyfile, certfile, keysize='1024', config=None, subject=None, cn=None)¶ Generate selfsigned SSL keypair
You must provide one of the 3 optional arguments: config, subject or cn If more than one is provided the leftmost will be used
Arguments: keyfile – (required) full path to the keyfile to be created certfile – (required) full path to the certfile to be created keysize – (optional) SSL key length config – (optional) openssl configuration file subject – (optional) dictionary with SSL subject variables cn – (optional) cerfificate common name
Required keys in subject dict: cn – Common name (eq. FQDN)
Optional keys in subject dict country – Country Name (2 letter code) state – State or Province Name (full name) locality – Locality Name (eg, city) organization – Organization Name (eg, company) organizational_unit – Organizational Unit Name (eg, section) email – Email Address
charmhelpers.contrib.storage package¶
charmhelpers.contrib.storage.linux package¶
charmhelpers.contrib.storage.linux.ceph module¶
-
class
charmhelpers.contrib.storage.linux.ceph.
CephBrokerRq
(api_version=1)¶ Bases:
object
Ceph broker request.
Multiple operations can be added to a request and sent to the Ceph broker to be executed.
Request is json-encoded for sending over the wire.
The API is versioned and defaults to version 1.
-
add_op_create_pool
(name, replica_count=3)¶
-
request
¶
-
-
class
charmhelpers.contrib.storage.linux.ceph.
CephBrokerRsp
(encoded_rsp)¶ Bases:
object
Ceph broker response.
Response is json-decoded and contents provided as methods/properties.
The API is versioned and defaults to version 1.
-
exit_code
¶
-
exit_msg
¶
-
-
charmhelpers.contrib.storage.linux.ceph.
ceph_version
()¶ Retrieve the local version of ceph.
-
charmhelpers.contrib.storage.linux.ceph.
configure
(service, key, auth, use_syslog)¶ Perform basic configuration of Ceph.
-
charmhelpers.contrib.storage.linux.ceph.
copy_files
(src, dst, symlinks=False, ignore=None)¶ Copy files from src to dst.
-
charmhelpers.contrib.storage.linux.ceph.
create_key_file
(service, key)¶ Create a file containing key.
-
charmhelpers.contrib.storage.linux.ceph.
create_keyring
(service, key)¶ Create a new Ceph keyring containing key.
-
charmhelpers.contrib.storage.linux.ceph.
create_pool
(service, name, replicas=3)¶ Create a new RADOS pool.
-
charmhelpers.contrib.storage.linux.ceph.
create_rbd_image
(service, pool, image, sizemb)¶ Create a new RADOS block device.
-
charmhelpers.contrib.storage.linux.ceph.
delete_keyring
(service)¶ Delete an existing Ceph keyring.
-
charmhelpers.contrib.storage.linux.ceph.
delete_pool
(service, name)¶ Delete a RADOS pool from ceph.
-
charmhelpers.contrib.storage.linux.ceph.
ensure_ceph_keyring
(service, user=None, group=None)¶ Ensures a ceph keyring is created for a named service and optionally ensures user and group ownership.
Returns False if no ceph key is available in relation state.
-
charmhelpers.contrib.storage.linux.ceph.
ensure_ceph_storage
(service, pool, rbd_img, sizemb, mount_point, blk_device, fstype, system_services=[], replicas=3)¶ NOTE: This function must only be called from a single service unit for the same rbd_img otherwise data loss will occur.
Ensures given pool and RBD image exists, is mapped to a block device, and the device is formatted and mounted at the given mount_point.
If formatting a device for the first time, data existing at mount_point will be migrated to the RBD device before being re-mounted.
All services listed in system_services will be stopped prior to data migration and restarted when complete.
-
charmhelpers.contrib.storage.linux.ceph.
filesystem_mounted
(fs)¶ Determine whether a filesytems is already mounted.
-
charmhelpers.contrib.storage.linux.ceph.
get_ceph_nodes
()¶ Query named relation ‘ceph’ to determine current nodes.
-
charmhelpers.contrib.storage.linux.ceph.
get_osds
(service)¶ Return a list of all Ceph Object Storage Daemons currently in the cluster.
-
charmhelpers.contrib.storage.linux.ceph.
image_mapped
(name)¶ Determine whether a RADOS block device is mapped locally.
-
charmhelpers.contrib.storage.linux.ceph.
install
()¶ Basic Ceph client installation.
-
charmhelpers.contrib.storage.linux.ceph.
make_filesystem
(blk_device, fstype='ext4', timeout=10)¶ Make a new filesystem on the specified block device.
-
charmhelpers.contrib.storage.linux.ceph.
map_block_storage
(service, pool, image)¶ Map a RADOS block device for local use.
-
charmhelpers.contrib.storage.linux.ceph.
modprobe
(module)¶ Load a kernel module and configure for auto-load on reboot.
-
charmhelpers.contrib.storage.linux.ceph.
place_data_on_block_device
(blk_device, data_src_dst)¶ Migrate data in data_src_dst to blk_device and then remount.
-
charmhelpers.contrib.storage.linux.ceph.
pool_exists
(service, name)¶ Check to see if a RADOS pool already exists.
-
charmhelpers.contrib.storage.linux.ceph.
rbd_exists
(service, pool, rbd_img)¶ Check to see if a RADOS block device exists.
charmhelpers.contrib.storage.linux.loopback module¶
-
charmhelpers.contrib.storage.linux.loopback.
create_loopback
(file_path)¶ Create a loopback device for a given backing file.
Returns: str: Full path to new loopback device (eg, /dev/loop0)
-
charmhelpers.contrib.storage.linux.loopback.
ensure_loopback_device
(path, size)¶ Ensure a loopback device exists for a given backing file path and size. If it a loopback device is not mapped to file, a new one will be created.
TODO: Confirm size of found loopback device.
Returns: str: Full path to the ensured loopback device (eg, /dev/loop0)
-
charmhelpers.contrib.storage.linux.loopback.
loopback_devices
()¶ Parse through ‘losetup -a’ output to determine currently mapped loopback devices. Output is expected to look like:
/dev/loop0: [0807]:961814 (/tmp/my.img)Returns: dict: a dict mapping {loopback_dev: backing_file}
charmhelpers.contrib.storage.linux.lvm module¶
-
charmhelpers.contrib.storage.linux.lvm.
create_lvm_physical_volume
(block_device)¶ Initialize a block device as an LVM physical volume.
Parameters: block_device – str: Full path of block device to initialize.
-
charmhelpers.contrib.storage.linux.lvm.
create_lvm_volume_group
(volume_group, block_device)¶ Create an LVM volume group backed by a given block device.
Assumes block device has already been initialized as an LVM PV.
Parameters: volume_group – str: Name of volume group to create. Block_device: str: Full path of PV-initialized block device.
-
charmhelpers.contrib.storage.linux.lvm.
deactivate_lvm_volume_group
(block_device)¶ Deactivate any volume gruop associated with an LVM physical volume.
Parameters: block_device – str: Full path to LVM physical volume
-
charmhelpers.contrib.storage.linux.lvm.
is_lvm_physical_volume
(block_device)¶ Determine whether a block device is initialized as an LVM PV.
Parameters: block_device – str: Full path of block device to inspect. Returns: boolean: True if block device is a PV, False if not.
-
charmhelpers.contrib.storage.linux.lvm.
list_lvm_volume_group
(block_device)¶ List LVM volume group associated with a given block device.
Assumes block device is a valid LVM PV.
Parameters: block_device – str: Full path of block device to inspect. Returns: str: Name of volume group associated with block device or None
-
charmhelpers.contrib.storage.linux.lvm.
remove_lvm_physical_volume
(block_device)¶ Remove LVM PV signatures from a given block device.
Parameters: block_device – str: Full path of block device to scrub.
charmhelpers.contrib.storage.linux.utils module¶
-
charmhelpers.contrib.storage.linux.utils.
is_block_device
(path)¶ Confirm device at path is a valid block device node.
Returns: boolean: True if path is a block device, False if not.
-
charmhelpers.contrib.storage.linux.utils.
is_device_mounted
(device)¶ Given a device path, return True if that device is mounted, and False if it isn’t.
Parameters: device – str: Full path of the device to check. Returns: boolean: True if the path represents a mounted device, False if it doesn’t.
-
charmhelpers.contrib.storage.linux.utils.
zap_disk
(block_device)¶ Clear a block device of partition table. Relies on sgdisk, which is installed as pat of the ‘gdisk’ package in Ubuntu.
Parameters: block_device – str: Full path of block device to clean.
charmhelpers.contrib.templating package¶
charmhelpers.contrib.templating.contexts module¶
A helper to create a yaml cache of config with namespaced relation data.
-
charmhelpers.contrib.templating.contexts.
dict_keys_without_hyphens
(a_dict)¶ Return the a new dict with underscores instead of hyphens in keys.
-
charmhelpers.contrib.templating.contexts.
juju_state_to_yaml
(yaml_path, namespace_separator=':', allow_hyphens_in_keys=True, mode=None)¶ Update the juju config and state in a yaml file.
This includes any current relation-get data, and the charm directory.
This function was created for the ansible and saltstack support, as those libraries can use a yaml file to supply context to templates, but it may be useful generally to create and update an on-disk cache of all the config, including previous relation data.
By default, hyphens are allowed in keys as this is supported by yaml, but for tools like ansible, hyphens are not valid [1].
[1] http://www.ansibleworks.com/docs/playbooks_variables.html#what-makes-a-valid-variable-name
-
charmhelpers.contrib.templating.contexts.
update_relations
(context, namespace_separator=':')¶ Update the context with the relation data.
charmhelpers.contrib.unison package¶
-
charmhelpers.contrib.unison.
collect_authed_hosts
(peer_interface)¶ Iterate through the units on peer interface to find all that have the calling host in its authorized hosts list
-
charmhelpers.contrib.unison.
create_private_key
(user, priv_key_path)¶
-
charmhelpers.contrib.unison.
create_public_key
(user, priv_key_path, pub_key_path)¶
-
charmhelpers.contrib.unison.
ensure_user
(user, group=None)¶
-
charmhelpers.contrib.unison.
get_homedir
(user)¶
-
charmhelpers.contrib.unison.
get_keypair
(user)¶
-
charmhelpers.contrib.unison.
run_as_user
(user, cmd, gid=None)¶
Main setup function, should be called from both peer -changed and -joined hooks with the same parameters.
-
charmhelpers.contrib.unison.
sync_path_to_host
(path, host, user, verbose=False, cmd=None, gid=None, fatal=False)¶ Sync path to an specific peer host
Propagates exception if operation fails and fatal=True.
-
charmhelpers.contrib.unison.
sync_to_peer
(host, user, paths=None, verbose=False, cmd=None, gid=None, fatal=False)¶ Sync paths to an specific peer host
Propagates exception if any operation fails and fatal=True.
-
charmhelpers.contrib.unison.
sync_to_peers
(peer_interface, user, paths=None, verbose=False, cmd=None, gid=None, fatal=False)¶ Sync all hosts to an specific path
The type of group is integer, it allows user has permissions to operate a directory have a different group id with the user id.
Propagates exception if any operation fails and fatal=True.
-
charmhelpers.contrib.unison.
write_known_hosts
(user, hosts)¶
charmhelpers.fetch package¶
charmhelpers.fetch.archiveurl module¶
-
class
charmhelpers.fetch.archiveurl.
ArchiveUrlFetchHandler
¶ Bases:
charmhelpers.fetch.BaseFetchHandler
Handler to download archive files from arbitrary URLs.
Can fetch from http, https, ftp, and file URLs.
Can install either tarballs (.tar, .tgz, .tbz2, etc) or zip files.
Installs the contents of the archive in $CHARM_DIR/fetched/.
-
can_handle
(source)¶
-
download
(source, dest)¶ Download an archive file.
Parameters: - source (str) – URL pointing to an archive file.
- dest (str) – Local path location to download archive file to.
-
download_and_validate
(url, hashsum, validate='sha1')¶
-
install
(source, dest=None, checksum=None, hash_type='sha1')¶ Download and install an archive file, with optional checksum validation.
The checksum can also be given on the source URL’s fragment. For example:
handler.install('http://example.com/file.tgz#sha1=deadbeef')
Parameters: - source (str) – URL pointing to an archive file.
- dest (str) – Local destination path to install to. If not given, installs to $CHARM_DIR/archives/archive_file_name.
- checksum (str) – If given, validate the archive file after download.
- hash_type (str) – Algorithm used to generate checksum.
Can be any hash alrgorithm supported by
hashlib
, such as md5, sha1, sha256, sha512, etc.
-
-
charmhelpers.fetch.archiveurl.
splitpasswd
(user)¶ urllib.splitpasswd(), but six’s support of this is missing
-
charmhelpers.fetch.archiveurl.
splituser
(host)¶ urllib.splituser(), but six’s support of this seems broken
charmhelpers.fetch.bzrurl module¶
-
exception
charmhelpers.fetch.
AptLockError
¶ Bases:
exceptions.Exception
-
class
charmhelpers.fetch.
BaseFetchHandler
¶ Bases:
object
Base class for FetchHandler implementations in fetch plugins
-
base_url
(url)¶ Return url without querystring or fragment
-
can_handle
(source)¶ Returns True if the source can be handled. Otherwise returns a string explaining why it cannot
-
install
(source)¶ Try to download and unpack the source. Return the path to the unpacked files or raise UnhandledSource.
-
parse_url
(url)¶
-
-
exception
charmhelpers.fetch.
SourceConfigError
¶ Bases:
exceptions.Exception
-
exception
charmhelpers.fetch.
UnhandledSource
¶ Bases:
exceptions.Exception
-
charmhelpers.fetch.
add_source
(source, key=None)¶ Add a package source to this system.
@param source: a URL or sources.list entry, as supported by add-apt-repository(1). Examples:
ppa:charmers/example deb https://stub:key@private.example.com/ubuntu trusty main
- In addition:
- ‘proposed:’ may be used to enable the standard ‘proposed’ pocket for the release. ‘cloud:’ may be used to activate official cloud archive pockets, such as ‘cloud:icehouse’ ‘distro’ may be used as a noop
@param key: A key to be added to the system’s APT keyring and used to verify the signatures on packages. Ideally, this should be an ASCII format GPG public key including the block headers. A GPG key id may also be used, but be aware that only insecure protocols are available to retrieve the actual public key from a public keyserver placing your Juju environment at risk. ppa and cloud archive keys are securely added automtically, so sould not be provided.
-
charmhelpers.fetch.
apt_cache
(in_memory=True)¶ Build and return an apt cache
-
charmhelpers.fetch.
apt_hold
(packages, fatal=False)¶
-
charmhelpers.fetch.
apt_install
(packages, options=None, fatal=False)¶ Install one or more packages
-
charmhelpers.fetch.
apt_mark
(packages, mark, fatal=False)¶ Flag one or more packages using apt-mark
-
charmhelpers.fetch.
apt_purge
(packages, fatal=False)¶ Purge one or more packages
-
charmhelpers.fetch.
apt_unhold
(packages, fatal=False)¶
-
charmhelpers.fetch.
apt_update
(fatal=False)¶ Update local apt cache
-
charmhelpers.fetch.
apt_upgrade
(options=None, fatal=False, dist=False)¶ Upgrade all packages
-
charmhelpers.fetch.
configure_sources
(update=False, sources_var='install_sources', keys_var='install_keys')¶ Configure multiple sources from charm configuration.
The lists are encoded as yaml fragments in the configuration. The frament needs to be included as a string. Sources and their corresponding keys are of the types supported by add_source().
- Example config:
- install_sources: |
- “ppa:foo”
- “http://example.com/repo precise main”
- install_keys: |
- null
- “a1b2c3d4”
Note that ‘null’ (a.k.a. None) should not be quoted.
-
charmhelpers.fetch.
filter_installed_packages
(packages)¶ Returns a list of packages that require installation
-
charmhelpers.fetch.
install_from_config
(config_var_name)¶
-
charmhelpers.fetch.
install_remote
(source, *args, **kwargs)¶ Install a file tree from a remote source
- The specified source should be a url of the form:
- scheme://[host]/path[#[option=value][&...]]
Schemes supported are based on this modules submodules. Options supported are submodule-specific. Additional arguments are passed through to the submodule.
For example:
dest = install_remote('http://example.com/archive.tgz', checksum='deadbeef', hash_type='sha1')
This will download archive.tgz, validate it using SHA1 and, if the file is ok, extract it and return the directory in which it was extracted. If the checksum fails, it will raise
charmhelpers.core.host.ChecksumError
.
-
charmhelpers.fetch.
plugins
(fetch_handlers=None)¶
charmhelpers.payload package¶
charmhelpers.payload.archive module¶
-
exception
charmhelpers.payload.archive.
ArchiveError
¶ Bases:
exceptions.Exception
-
charmhelpers.payload.archive.
archive_dest_default
(archive_name)¶
-
charmhelpers.payload.archive.
extract
(archive_name, destpath=None)¶
-
charmhelpers.payload.archive.
extract_tarfile
(archive_name, destpath)¶ Unpack a tar archive, optionally compressed
-
charmhelpers.payload.archive.
extract_zipfile
(archive_name, destpath)¶ Unpack a zip file
-
charmhelpers.payload.archive.
get_archive_handler
(archive_name)¶
charmhelpers.payload.execd module¶
-
charmhelpers.payload.execd.
default_execd_dir
()¶
-
charmhelpers.payload.execd.
execd_module_paths
(execd_dir=None)¶ Generate a list of full paths to modules within execd_dir.
-
charmhelpers.payload.execd.
execd_preinstall
(execd_dir=None)¶ Run charm-pre-install for each module within execd_dir.
-
charmhelpers.payload.execd.
execd_run
(command, execd_dir=None, die_on_error=False, stderr=None)¶ Run command for each module within execd_dir which defines it.
-
charmhelpers.payload.execd.
execd_submodule_paths
(command, execd_dir=None)¶ Generate a list of full paths to the specified command within exec_dir.
Tools for working with files injected into a charm just before deployment.
charmhelpers.cli package¶
charmhelpers.cli.commands module¶
This module loads sub-modules into the python runtime so they can be discovered via the inspect module. In order to prevent flake8 from (rightfully) telling us these are unused modules, throw a ‘ # noqa’ at the end of each import so that the warning is suppressed.
charmhelpers.cli.host module¶
-
charmhelpers.cli.host.
mounts
()¶ List mounts
-
class
charmhelpers.cli.
CommandLine
¶ Bases:
object
-
argument_parser
= None¶
-
exit_code
= 0¶
-
formatter
= None¶
-
no_output
(decorated)¶ Subcommand is not expected to return a value, so don’t print a spurious None.
-
run
()¶ Run cli, processing arguments and executing subcommands.
-
subcommand
(command_name=None)¶ Decorate a function as a subcommand. Use its arguments as the command-line arguments
-
subcommand_builder
(command_name, description=None)¶ Decorate a function that builds a subcommand. Builders should accept a single argument (the subparser instance) and return the function to be run as the command.
-
subparsers
= None¶
-
test_command
(decorated)¶ Subcommand is a boolean test function, so bool return values should be converted to a 0/1 exit code.
-
-
class
charmhelpers.cli.
OutputFormatter
(outfile=<open file '<stdout>', mode 'w'>)¶ Bases:
object
-
add_arguments
(argument_parser)¶
-
csv
(output)¶ Output data as excel-compatible CSV
-
format_output
(output, fmt='raw')¶
-
json
(output)¶ Output data in JSON format
-
py
(output)¶ Output data as a nicely-formatted python data structure
-
raw
(output)¶ Output data as raw string (default)
-
supported_formats
¶
-
tab
(output)¶ Output data in excel-compatible tab-delimited format
-
yaml
(output)¶ Output data in YAML format
-
-
charmhelpers.cli.
describe_arguments
(func)¶ Analyze a function’s signature and return a data structure suitable for passing in as arguments to an argparse parser’s add_argument() method.
charmhelpers.coordinator package¶
charmhelpers.coordinator module¶
The coordinator module allows you to use Juju’s leadership feature to coordinate operations between units of a service.
Behavior is defined in subclasses of coordinator.BaseCoordinator. One implementation is provided (coordinator.Serial), which allows an operation to be run on a single unit at a time, on a first come, first served basis. You can trivially define more complex behavior by subclassing BaseCoordinator or Serial.
author: | Stuart Bishop <stuart.bishop@canonical.com> |
---|
Services Framework Usage¶
Ensure a peer relation is defined in metadata.yaml. Instantiate a BaseCoordinator subclass before invoking ServiceManager.manage(). Ensure that ServiceManager.manage() is wired up to the leader-elected, leader-settings-changed, peer relation-changed and peer relation-departed hooks in addition to any other hooks you need, or your service will deadlock.
Ensure calls to acquire() are guarded, so that locks are only requested when they are really needed (and thus hooks only triggered when necessary). Failing to do this and calling acquire() unconditionally will put your unit into a hook loop. Calls to granted() do not need to be guarded.
For example:
from charmhelpers.core import hookenv, services
from charmhelpers import coordinator
def maybe_restart(servicename):
serial = coordinator.Serial()
if needs_restart():
serial.acquire('restart')
if serial.granted('restart'):
hookenv.service_restart(servicename)
services = [dict(service='servicename',
data_ready=[maybe_restart])]
if __name__ == '__main__':
_ = coordinator.Serial() # Must instantiate before manager.manage()
manager = services.ServiceManager(services)
manager.manage()
You can implement a similar pattern using a decorator. If the lock has not been granted, an attempt to acquire() it will be made if the guard function returns True. If the lock has been granted, the decorated function is run as normal:
from charmhelpers.core import hookenv, services
from charmhelpers import coordinator
serial = coordinator.Serial() # Global, instatiated on module import.
def needs_restart():
[ ... Introspect state. Return True if restart is needed ... ]
@serial.require('restart', needs_restart)
def maybe_restart(servicename):
hookenv.service_restart(servicename)
services = [dict(service='servicename',
data_ready=[maybe_restart])]
if __name__ == '__main__':
manager = services.ServiceManager(services)
manager.manage()
Traditional Usage¶
Ensure a peer relationis defined in metadata.yaml.
If you are using charmhelpers.core.hookenv.Hooks, ensure that a BaseCoordinator subclass is instantiated before calling Hooks.execute.
If you are not using charmhelpers.core.hookenv.Hooks, ensure that a BaseCoordinator subclass is instantiated and its handle() method called at the start of all your hooks.
For example:
import sys
from charmhelpers.core import hookenv
from charmhelpers import coordinator
hooks = hookenv.Hooks()
def maybe_restart():
serial = coordinator.Serial()
if serial.granted('restart'):
hookenv.service_restart('myservice')
@hooks.hook
def config_changed():
update_config()
serial = coordinator.Serial()
if needs_restart():
serial.acquire('restart'):
maybe_restart()
# Cluster hooks must be wired up.
@hooks.hook('cluster-relation-changed', 'cluster-relation-departed')
def cluster_relation_changed():
maybe_restart()
# Leader hooks must be wired up.
@hooks.hook('leader-elected', 'leader-settings-changed')
def leader_settings_changed():
maybe_restart()
[ ... repeat for *all* other hooks you are using ... ]
if __name__ == '__main__':
_ = coordinator.Serial() # Must instantiate before execute()
hooks.execute(sys.argv)
You can also use the require decorator. If the lock has not been granted, an attempt to acquire() it will be made if the guard function returns True. If the lock has been granted, the decorated function is run as normal:
from charmhelpers.core import hookenv
hooks = hookenv.Hooks()
serial = coordinator.Serial() # Must instantiate before execute()
@require('restart', needs_restart)
def maybe_restart():
hookenv.service_restart('myservice')
@hooks.hook('install', 'config-changed', 'upgrade-charm',
# Peer and leader hooks must be wired up.
'cluster-relation-changed', 'cluster-relation-departed',
'leader-elected', 'leader-settings-changed')
def default_hook():
[...]
maybe_restart()
if __name__ == '__main__':
hooks.execute()
Details¶
A simple API is provided similar to traditional locking APIs. A lock may be requested using the acquire() method, and the granted() method may be used do to check if a lock previously requested by acquire() has been granted. It doesn’t matter how many times acquire() is called in a hook.
Locks are released at the end of the hook they are acquired in. This may be the current hook if the unit is leader and the lock is free. It is more likely a future hook (probably leader-settings-changed, possibly the peer relation-changed or departed hook, potentially any hook).
Whenever a charm needs to perform a coordinated action it will acquire() the lock and perform the action immediately if acquisition is successful. It will also need to perform the same action in every other hook if the lock has been granted.
Grubby Details¶
Why do you need to be able to perform the same action in every hook? If the unit is the leader, then it may be able to grant its own lock and perform the action immediately in the source hook. If the unit is the leader and cannot immediately grant the lock, then its only guaranteed chance of acquiring the lock is in the peer relation-joined, relation-changed or peer relation-departed hooks when another unit has released it (the only channel to communicate to the leader is the peer relation). If the unit is not the leader, then it is unlikely the lock is granted in the source hook (a previous hook must have also made the request for this to happen). A non-leader is notified about the lock via leader settings. These changes may be visible in any hook, even before the leader-settings-changed hook has been invoked. Or the requesting unit may be promoted to leader after making a request, in which case the lock may be granted in leader-elected or in a future peer relation-changed or relation-departed hook.
This could be simpler if leader-settings-changed was invoked on the leader. We could then never grant locks except in leader-settings-changed hooks giving one place for the operation to be performed. Unfortunately this is not the case with Juju 1.23 leadership.
But of course, this doesn’t really matter to most people as most people seem to prefer the Services Framework or similar reset-the-world approaches, rather than the twisty maze of attempting to deduce what should be done based on what hook happens to be running (which always seems to evolve into reset-the-world anyway when the charm grows beyond the trivial).
I chose not to implement a callback model, where a callback was passed to acquire to be executed when the lock is granted, because the callback may become invalid between making the request and the lock being granted due to an upgrade-charm being run in the interim. And it would create restrictions, such no lambdas, callback defined at the top level of a module, etc. Still, we could implement it on top of what is here, eg. by adding a defer decorator that stores a pickle of itself to disk and have BaseCoordinator unpickle and execute them when the locks are granted.
-
class
charmhelpers.coordinator.
BaseCoordinator
(relation_key='coordinator', peer_relation_name=None)¶ Bases:
object
-
acquire
(lock)¶ Acquire the named lock, non-blocking.
The lock may be granted immediately, or in a future hook.
Returns True if the lock has been granted. The lock will be automatically released at the end of the hook in which it is granted.
Do not mindlessly call this method, as it triggers a cascade of hooks. For example, if you call acquire() every time in your peer relation-changed hook you will end up with an infinite loop of hooks. It should almost always be guarded by some condition.
-
grant
(lock, unit)¶ Maybe grant the lock to a unit.
The decision to grant the lock or not is made for $lock by a corresponding method grant_$lock, which you may define in a subclass. If no such method is defined, the default_grant method is used. See Serial.default_grant() for details.
-
granted
(lock)¶ Return True if a previously requested lock has been granted
-
grants
= None¶
-
handle
()¶
-
initialize
()¶
-
msg
(msg)¶ Emit a message. Override to customize log spam.
-
released
(unit, lock, timestamp)¶ Called on the leader when it has released a lock.
By default, does nothing but log messages. Override if you need to perform additional housekeeping when a lock is released, for example recording timestamps.
-
relid
= None¶
-
relname
= None¶
-
request_timestamp
(lock)¶ Return the timestamp of our outstanding request for lock, or None.
Returns a datetime.datetime() UTC timestamp, with no tzinfo attribute.
-
requested
(lock)¶ Return True if we are in the queue for the lock
-
requests
= None¶
-
require
(lock, guard_func, *guard_args, **guard_kw)¶ Decorate a function to be run only when a lock is acquired.
The lock is requested if the guard function returns True.
The decorated function is called if the lock has been granted.
-
-
class
charmhelpers.coordinator.
Serial
(relation_key='coordinator', peer_relation_name=None)¶ Bases:
charmhelpers.coordinator.BaseCoordinator
-
default_grant
(lock, unit, granted, queue)¶ Default logic to grant a lock to a unit. Unless overridden, only one unit may hold the lock and it will be granted to the earliest queued request.
To define custom logic for $lock, create a subclass and define a grant_$lock method.
unit is the unit name making the request.
granted is the set of units already granted the lock. It will never include unit. It may be empty.
queue is the list of units waiting for the lock, ordered by time of request. It will always include unit, but unit is not necessarily first.
Returns True if the lock should be granted to unit.
-
-
class
charmhelpers.coordinator.
Singleton
¶ Bases:
type