charm-glance/hooks/glance_contexts.py

298 lines
9.4 KiB
Python

# Copyright 2016 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
from charmhelpers.core.strutils import (
bytes_from_string
)
from charmhelpers.core.hookenv import (
is_relation_made,
relation_ids,
relation_get,
related_units,
service_name,
config,
log as juju_log,
ERROR
)
from charmhelpers.contrib.openstack.context import (
OSContextGenerator,
ApacheSSLContext as SSLContext,
BindHostContext
)
from charmhelpers.contrib.hahelpers.cluster import (
determine_apache_port,
determine_api_port,
)
from charmhelpers.contrib.openstack.utils import (
os_release,
CompareOpenStackReleases,
)
class GlanceContext(OSContextGenerator):
def __call__(self):
ctxt = {
'disk_formats': config('disk-formats')
}
if config('container-formats'):
ctxt['container_formats'] = config('container-formats')
if config('filesystem-store-datadir'):
ctxt['filesystem_store_datadir'] = (
config('filesystem-store-datadir'))
image_size_cap = config('image-size-cap')
if image_size_cap:
try:
ctxt['image_size_cap'] = bytes_from_string(
image_size_cap.replace(' ', '').upper())
except (ValueError, KeyError):
juju_log('Unable to parse value for image-size-cap ({}), '
'see config.yaml for information about valid '
'formatting'.format(config('image-size-cap')),
level=ERROR)
raise
return ctxt
class GlancePolicyContext(OSContextGenerator):
"""This Context is only used from Ussuri onwards. At Ussuri, Glance
implemented policy-in-code, and thus didn't ship with a policy.json.
Therefore, the charm introduces a 'policy.yaml' file that is used to
provide the override here.
Note that this is separate from policy overrides as it's a charm config
option that has existed prior to its introduction.
Update *_image_location policy to restrict to admin role.
We do this unconditonally and keep a record of the original as installed by
the package.
"""
def __call__(self):
if config('restrict-image-location-operations'):
policy_value = 'role:admin'
else:
policy_value = ''
ctxt = {
"get_image_location": policy_value,
"set_image_location": policy_value,
"delete_image_location": policy_value,
}
return ctxt
class CephGlanceContext(OSContextGenerator):
interfaces = ['ceph-glance']
def __call__(self):
"""Used to generate template context to be added to glance-api.conf in
the presence of a ceph relation.
"""
if not is_relation_made(relation="ceph",
keys="key"):
return {}
service = service_name()
if config('pool-type') == 'erasure-coded':
pool_name = (
config('ec-rbd-metadata-pool') or
"{}-metadata".format(config('rbd-pool-name') or
service)
)
else:
if config('rbd-pool-name'):
pool_name = config('rbd-pool-name')
else:
pool_name = service
return {
# pool created based on service name.
'rbd_pool': pool_name,
'rbd_user': service,
'expose_image_locations': config('expose-image-locations')
}
class ObjectStoreContext(OSContextGenerator):
interfaces = ['object-store']
def __call__(self):
"""Object store config.
Used to generate template context to be added to glance-api.conf in
the presence of a 'object-store' relation.
"""
if not relation_ids('object-store'):
return {}
return {
'swift_store': True,
}
class CinderStoreContext(OSContextGenerator):
interfaces = ['cinder-volume-service', 'storage-backend']
def __call__(self):
"""Cinder store config.
Used to generate template context to be added to glance-api.conf in
the presence of a 'cinder-volume-service' relation or in the
presence of a flag 'cinder-backend' in the 'storage-backend' relation.
"""
if relation_ids('cinder-volume-service'):
return {'cinder_store': True}
for rid in relation_ids('storage-backend'):
for unit in related_units(rid):
value = relation_get('cinder-backend', rid=rid, unit=unit)
# value is a boolean flag
return {'cinder_store': value}
return {}
class MultiBackendContext(OSContextGenerator):
def _get_ceph_config(self):
ceph_ctx = CephGlanceContext()()
if not ceph_ctx:
return
ctx = {
"rbd_store_chunk_size": 8,
"rbd_store_pool": ceph_ctx["rbd_pool"],
"rbd_store_user": ceph_ctx["rbd_user"],
"rados_connect_timeout": 0,
"rbd_store_ceph_conf": "/etc/ceph/ceph.conf",
}
return ctx
def _get_swift_config(self):
swift_ctx = ObjectStoreContext()()
if not swift_ctx or swift_ctx.get("swift_store", False) is False:
return
ctx = {
"default_swift_reference": "swift",
"swift_store_config_file": "/etc/glance/glance-swift.conf",
"swift_store_create_container_on_put": "true",
}
return ctx
def __call__(self):
ctxt = {
"enabled_backend_configs": {},
"enabled_backends": None,
"default_store_backend": None,
}
backends = []
local_fs = config('filesystem-store-datadir')
if local_fs:
backends.append("local:file")
ctxt["enabled_backend_configs"]["local"] = {
"filesystem_store_datadir": local_fs,
"store_description": "Local filesystem store",
}
ceph_ctx = self._get_ceph_config()
if ceph_ctx:
backends.append("ceph:rbd")
ctxt["enabled_backend_configs"]["ceph"] = ceph_ctx
ctxt["default_store_backend"] = "ceph"
swift_ctx = self._get_swift_config()
if swift_ctx:
backends.append("swift:swift")
ctxt["enabled_backend_configs"]["swift"] = swift_ctx
if not ctxt["default_store_backend"]:
ctxt["default_store_backend"] = "swift"
if local_fs and not ctxt["default_store_backend"]:
ctxt["default_store_backend"] = "local"
if len(backends) > 0:
ctxt["enabled_backends"] = ", ".join(backends)
return ctxt
class MultiStoreContext(OSContextGenerator):
def __call__(self):
stores = ['glance.store.filesystem.Store', 'glance.store.http.Store']
store_mapping = {
'ceph': 'glance.store.rbd.Store',
'object-store': 'glance.store.swift.Store',
}
for store_relation, store_type in store_mapping.items():
if relation_ids(store_relation):
stores.append(store_type)
_release = os_release('glance-common')
if ((relation_ids('cinder-volume-service') or
relation_ids('storage-backend')) and
CompareOpenStackReleases(_release) >= 'mitaka'):
# even if storage-backend is present with cinder-backend=False it
# means that glance should not store images in cinder by default
# but can read images from cinder.
stores.append('glance.store.cinder.Store')
stores.sort()
return {
'known_stores': ','.join(stores)
}
class HAProxyContext(OSContextGenerator):
interfaces = ['cluster']
def __call__(self):
'''Extends the main charmhelpers HAProxyContext with a port mapping
specific to this charm.
Also used to extend glance-api.conf context with correct bind_port
'''
haproxy_port = 9292
apache_port = determine_apache_port(9292, singlenode_mode=True)
api_port = determine_api_port(9292, singlenode_mode=True)
ctxt = {
'service_ports': {'glance_api': [haproxy_port, apache_port]},
'bind_port': api_port,
}
return ctxt
class ApacheSSLContext(SSLContext):
interfaces = ['https']
external_ports = [9292]
service_namespace = 'glance'
def __call__(self):
return super(ApacheSSLContext, self).__call__()
class LoggingConfigContext(OSContextGenerator):
def __call__(self):
return {'debug': config('debug'), 'verbose': config('verbose')}
class GlanceIPv6Context(BindHostContext):
def __call__(self):
ctxt = super(GlanceIPv6Context, self).__call__()
if config('prefer-ipv6'):
ctxt['registry_host'] = '[::]'
else:
ctxt['registry_host'] = '0.0.0.0'
return ctxt