Sync charm helpers
This commit is contained in:
parent
ddebf2ef39
commit
2cc8ced26e
@ -23,7 +23,7 @@ from charmhelpers.contrib.storage.linux.lvm import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
from charmhelpers.core.host import lsb_release, mounts, umount
|
from charmhelpers.core.host import lsb_release, mounts, umount
|
||||||
from charmhelpers.fetch import apt_install
|
from charmhelpers.fetch import apt_install, apt_cache
|
||||||
from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
|
from charmhelpers.contrib.storage.linux.utils import is_block_device, zap_disk
|
||||||
from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device
|
from charmhelpers.contrib.storage.linux.loopback import ensure_loopback_device
|
||||||
|
|
||||||
@ -134,13 +134,8 @@ def get_os_version_codename(codename):
|
|||||||
def get_os_codename_package(package, fatal=True):
|
def get_os_codename_package(package, fatal=True):
|
||||||
'''Derive OpenStack release codename from an installed package.'''
|
'''Derive OpenStack release codename from an installed package.'''
|
||||||
import apt_pkg as apt
|
import apt_pkg as apt
|
||||||
apt.init()
|
|
||||||
|
|
||||||
# Tell apt to build an in-memory cache to prevent race conditions (if
|
cache = apt_cache()
|
||||||
# another process is already building the cache).
|
|
||||||
apt.config.set("Dir::Cache::pkgcache", "")
|
|
||||||
|
|
||||||
cache = apt.Cache()
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
pkg = cache[package]
|
pkg = cache[package]
|
||||||
|
@ -285,8 +285,9 @@ def relation_get(attribute=None, unit=None, rid=None):
|
|||||||
raise
|
raise
|
||||||
|
|
||||||
|
|
||||||
def relation_set(relation_id=None, relation_settings={}, **kwargs):
|
def relation_set(relation_id=None, relation_settings=None, **kwargs):
|
||||||
"""Set relation information for the current unit"""
|
"""Set relation information for the current unit"""
|
||||||
|
relation_settings = relation_settings if relation_settings else {}
|
||||||
relation_cmd_line = ['relation-set']
|
relation_cmd_line = ['relation-set']
|
||||||
if relation_id is not None:
|
if relation_id is not None:
|
||||||
relation_cmd_line.extend(('-r', relation_id))
|
relation_cmd_line.extend(('-r', relation_id))
|
||||||
|
@ -332,13 +332,9 @@ def cmp_pkgrevno(package, revno, pkgcache=None):
|
|||||||
|
|
||||||
'''
|
'''
|
||||||
import apt_pkg
|
import apt_pkg
|
||||||
|
from charmhelpers.fetch import apt_cache
|
||||||
if not pkgcache:
|
if not pkgcache:
|
||||||
apt_pkg.init()
|
pkgcache = apt_cache()
|
||||||
# Force Apt to build its cache in memory. That way we avoid race
|
|
||||||
# conditions with other applications building the cache in the same
|
|
||||||
# place.
|
|
||||||
apt_pkg.config.set("Dir::Cache::pkgcache", "")
|
|
||||||
pkgcache = apt_pkg.Cache()
|
|
||||||
pkg = pkgcache[package]
|
pkg = pkgcache[package]
|
||||||
return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)
|
return apt_pkg.version_compare(pkg.current_ver.ver_str, revno)
|
||||||
|
|
||||||
|
@ -17,20 +17,13 @@ class ServiceManager(object):
|
|||||||
"""
|
"""
|
||||||
Register a list of services, given their definitions.
|
Register a list of services, given their definitions.
|
||||||
|
|
||||||
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
|
|
||||||
ServiceManager framework tries to bring the focus to the data and the
|
|
||||||
setup tasks, in the most declarative way possible.
|
|
||||||
|
|
||||||
Service definitions are dicts in the following formats (all keys except
|
Service definitions are dicts in the following formats (all keys except
|
||||||
'service' are optional)::
|
'service' are optional)::
|
||||||
|
|
||||||
{
|
{
|
||||||
"service": <service name>,
|
"service": <service name>,
|
||||||
"required_data": <list of required data contexts>,
|
"required_data": <list of required data contexts>,
|
||||||
|
"provided_data": <list of provided data contexts>,
|
||||||
"data_ready": <one or more callbacks>,
|
"data_ready": <one or more callbacks>,
|
||||||
"data_lost": <one or more callbacks>,
|
"data_lost": <one or more callbacks>,
|
||||||
"start": <one or more callbacks>,
|
"start": <one or more callbacks>,
|
||||||
@ -44,6 +37,10 @@ class ServiceManager(object):
|
|||||||
of 'data_ready' and 'start' callbacks executed. See `is_ready()` for more
|
of 'data_ready' and 'start' callbacks executed. See `is_ready()` for more
|
||||||
information.
|
information.
|
||||||
|
|
||||||
|
The 'provided_data' list should contain relation data providers, most likely
|
||||||
|
a subclass of :class:`charmhelpers.core.services.helpers.RelationContext`,
|
||||||
|
that will indicate a set of data to set on a given relation.
|
||||||
|
|
||||||
The 'data_ready' value should be either a single callback, or a list of
|
The 'data_ready' value should be either a single callback, or a list of
|
||||||
callbacks, to be called when all items in 'required_data' pass `is_ready()`.
|
callbacks, to be called when all items in 'required_data' pass `is_ready()`.
|
||||||
Each callback will be called with the service name as the only parameter.
|
Each callback will be called with the service name as the only parameter.
|
||||||
@ -123,12 +120,20 @@ class ServiceManager(object):
|
|||||||
self.reconfigure_services()
|
self.reconfigure_services()
|
||||||
|
|
||||||
def provide_data(self):
|
def provide_data(self):
|
||||||
|
"""
|
||||||
|
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.
|
||||||
|
"""
|
||||||
hook_name = hookenv.hook_name()
|
hook_name = hookenv.hook_name()
|
||||||
for service in self.services.values():
|
for service in self.services.values():
|
||||||
for provider in service.get('provided_data', []):
|
for provider in service.get('provided_data', []):
|
||||||
if re.match(r'{}-relation-(joined|changed)'.format(provider.name), hook_name):
|
if re.match(r'{}-relation-(joined|changed)'.format(provider.name), hook_name):
|
||||||
data = provider.provide_data()
|
data = provider.provide_data()
|
||||||
if provider._is_ready(data):
|
_ready = provider._is_ready(data) if hasattr(provider, '_is_ready') else data
|
||||||
|
if _ready:
|
||||||
hookenv.relation_set(None, data)
|
hookenv.relation_set(None, data)
|
||||||
|
|
||||||
def reconfigure_services(self, *service_names):
|
def reconfigure_services(self, *service_names):
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import importlib
|
import importlib
|
||||||
|
from tempfile import NamedTemporaryFile
|
||||||
import time
|
import time
|
||||||
from yaml import safe_load
|
from yaml import safe_load
|
||||||
from charmhelpers.core.host import (
|
from charmhelpers.core.host import (
|
||||||
@ -116,15 +117,7 @@ class BaseFetchHandler(object):
|
|||||||
|
|
||||||
def filter_installed_packages(packages):
|
def filter_installed_packages(packages):
|
||||||
"""Returns a list of packages that require installation"""
|
"""Returns a list of packages that require installation"""
|
||||||
import apt_pkg
|
cache = apt_cache()
|
||||||
apt_pkg.init()
|
|
||||||
|
|
||||||
# Tell apt to build an in-memory cache to prevent race conditions (if
|
|
||||||
# another process is already building the cache).
|
|
||||||
apt_pkg.config.set("Dir::Cache::pkgcache", "")
|
|
||||||
apt_pkg.config.set("Dir::Cache::srcpkgcache", "")
|
|
||||||
|
|
||||||
cache = apt_pkg.Cache()
|
|
||||||
_pkgs = []
|
_pkgs = []
|
||||||
for package in packages:
|
for package in packages:
|
||||||
try:
|
try:
|
||||||
@ -137,6 +130,16 @@ def filter_installed_packages(packages):
|
|||||||
return _pkgs
|
return _pkgs
|
||||||
|
|
||||||
|
|
||||||
|
def apt_cache(in_memory=True):
|
||||||
|
"""Build and return an apt cache"""
|
||||||
|
import apt_pkg
|
||||||
|
apt_pkg.init()
|
||||||
|
if in_memory:
|
||||||
|
apt_pkg.config.set("Dir::Cache::pkgcache", "")
|
||||||
|
apt_pkg.config.set("Dir::Cache::srcpkgcache", "")
|
||||||
|
return apt_pkg.Cache()
|
||||||
|
|
||||||
|
|
||||||
def apt_install(packages, options=None, fatal=False):
|
def apt_install(packages, options=None, fatal=False):
|
||||||
"""Install one or more packages"""
|
"""Install one or more packages"""
|
||||||
if options is None:
|
if options is None:
|
||||||
@ -202,6 +205,27 @@ def apt_hold(packages, fatal=False):
|
|||||||
|
|
||||||
|
|
||||||
def add_source(source, key=None):
|
def 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'
|
||||||
|
|
||||||
|
@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.
|
||||||
|
"""
|
||||||
if source is None:
|
if source is None:
|
||||||
log('Source is not present. Skipping')
|
log('Source is not present. Skipping')
|
||||||
return
|
return
|
||||||
@ -226,10 +250,23 @@ def add_source(source, key=None):
|
|||||||
release = lsb_release()['DISTRIB_CODENAME']
|
release = lsb_release()['DISTRIB_CODENAME']
|
||||||
with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
|
with open('/etc/apt/sources.list.d/proposed.list', 'w') as apt:
|
||||||
apt.write(PROPOSED_POCKET.format(release))
|
apt.write(PROPOSED_POCKET.format(release))
|
||||||
|
else:
|
||||||
|
raise SourceConfigError("Unknown source: {!r}".format(source))
|
||||||
|
|
||||||
if key:
|
if key:
|
||||||
subprocess.check_call(['apt-key', 'adv', '--keyserver',
|
if '-----BEGIN PGP PUBLIC KEY BLOCK-----' in key:
|
||||||
'hkp://keyserver.ubuntu.com:80', '--recv',
|
with NamedTemporaryFile() as key_file:
|
||||||
key])
|
key_file.write(key)
|
||||||
|
key_file.flush()
|
||||||
|
key_file.seek(0)
|
||||||
|
subprocess.check_call(['apt-key', 'add', '-'], stdin=key_file)
|
||||||
|
else:
|
||||||
|
# Note that hkp: is in no way a secure protocol. Using a
|
||||||
|
# GPG key id is pointless from a security POV unless you
|
||||||
|
# absolutely trust your network and DNS.
|
||||||
|
subprocess.check_call(['apt-key', 'adv', '--keyserver',
|
||||||
|
'hkp://keyserver.ubuntu.com:80', '--recv',
|
||||||
|
key])
|
||||||
|
|
||||||
|
|
||||||
def configure_sources(update=False,
|
def configure_sources(update=False,
|
||||||
@ -239,7 +276,8 @@ def configure_sources(update=False,
|
|||||||
Configure multiple sources from charm configuration.
|
Configure multiple sources from charm configuration.
|
||||||
|
|
||||||
The lists are encoded as yaml fragments in the configuration.
|
The lists are encoded as yaml fragments in the configuration.
|
||||||
The frament needs to be included as a string.
|
The frament needs to be included as a string. Sources and their
|
||||||
|
corresponding keys are of the types supported by add_source().
|
||||||
|
|
||||||
Example config:
|
Example config:
|
||||||
install_sources: |
|
install_sources: |
|
||||||
|
Loading…
x
Reference in New Issue
Block a user