charm-ceph-radosgw/hooks/ceph_rgw.py

200 lines
6.6 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.
import os
import subprocess
from charmhelpers.core.hookenv import (
config,
service_name,
)
from charmhelpers.core.host import (
mkdir,
symlink,
)
from charmhelpers.contrib.storage.linux.ceph import (
CephBrokerRq,
)
CEPH_DIR = '/etc/ceph'
CEPH_RADOSGW_DIR = '/var/lib/ceph/radosgw'
_radosgw_keyring = "keyring.rados.gateway"
CEPH_POOL_APP_NAME = 'rgw'
def import_radosgw_key(key, name=None):
if name:
keyring_path = os.path.join(CEPH_RADOSGW_DIR,
'ceph-{}'.format(name),
'keyring')
link_path = os.path.join(CEPH_DIR,
'ceph.client.{}.keyring'.format(name))
owner = group = 'ceph'
else:
keyring_path = os.path.join(CEPH_DIR, _radosgw_keyring)
link_path = None
owner = group = 'root'
if not os.path.exists(keyring_path):
mkdir(path=os.path.dirname(keyring_path),
owner=owner, group=group, perms=0o750)
cmd = [
'ceph-authtool',
keyring_path,
'--create-keyring',
'--name=client.{}'.format(
name or 'radosgw.gateway'
),
'--add-key={}'.format(key)
]
subprocess.check_call(cmd)
cmd = [
'chown',
'{}:{}'.format(owner, group),
keyring_path
]
subprocess.check_call(cmd)
# NOTE: add a link to the keyring in /var/lib/ceph
# to /etc/ceph so we can use it for radosgw-admin
# operations for multi-site configuration
if link_path:
symlink(keyring_path, link_path)
return True
return False
def get_create_rgw_pools_rq(prefix=None):
"""Pre-create RGW pools so that they have the correct settings.
If a prefix is provided it will be prepended to each pool name.
When RGW creates its own pools it will create them with non-optimal
settings (LP: #1476749).
NOTE: see http://docs.ceph.com/docs/master/radosgw/config-ref/#pools and
http://docs.ceph.com/docs/master/radosgw/config/#create-pools for
list of supported/required pools.
"""
def _add_light_pool(rq, pool, pg_num, prefix=None):
# Per the Ceph PG Calculator, all of the lightweight pools get 0.10%
# of the data by default and only the .rgw.buckets.* get higher values
weights = {
'.rgw.buckets.index': 3.00,
'.rgw.buckets.extra': 1.00
}
w = weights.get(pool, 0.10)
if prefix:
pool = "{prefix}{pool}".format(prefix=prefix, pool=pool)
if pg_num > 0:
rq.add_op_create_pool(name=pool, replica_count=replicas,
pg_num=pg_num, group='objects',
app_name=CEPH_POOL_APP_NAME)
else:
rq.add_op_create_pool(name=pool, replica_count=replicas,
weight=w, group='objects',
app_name=CEPH_POOL_APP_NAME)
rq = CephBrokerRq()
replicas = config('ceph-osd-replication-count')
prefix = prefix or 'default'
# Buckets likely to contain the most data and therefore
# requiring the most PGs
heavy = [
'.rgw.buckets.data'
]
bucket_weight = config('rgw-buckets-pool-weight')
if config('pool-type') == 'erasure-coded':
# General EC plugin config
plugin = config('ec-profile-plugin')
technique = config('ec-profile-technique')
device_class = config('ec-profile-device-class')
bdm_k = config('ec-profile-k')
bdm_m = config('ec-profile-m')
# LRC plugin config
bdm_l = config('ec-profile-locality')
crush_locality = config('ec-profile-crush-locality')
# SHEC plugin config
bdm_c = config('ec-profile-durability-estimator')
# CLAY plugin config
bdm_d = config('ec-profile-helper-chunks')
scalar_mds = config('ec-profile-scalar-mds')
# Profile name
service = service_name()
profile_name = (
config('ec-profile-name') or "{}-profile".format(service)
)
rq.add_op_create_erasure_profile(
name=profile_name,
k=bdm_k, m=bdm_m,
lrc_locality=bdm_l,
lrc_crush_locality=crush_locality,
shec_durability_estimator=bdm_c,
clay_helper_chunks=bdm_d,
clay_scalar_mds=scalar_mds,
device_class=device_class,
erasure_type=plugin,
erasure_technique=technique
)
for pool in heavy:
pool = "{prefix}{pool}".format(prefix=prefix, pool=pool)
rq.add_op_create_erasure_pool(
name=pool,
erasure_profile=profile_name,
weight=bucket_weight,
group="objects",
app_name=CEPH_POOL_APP_NAME
)
else:
for pool in heavy:
pool = "{prefix}{pool}".format(prefix=prefix, pool=pool)
rq.add_op_create_pool(name=pool, replica_count=replicas,
weight=bucket_weight, group='objects',
app_name=CEPH_POOL_APP_NAME)
# NOTE: we want these pools to have a smaller pg_num/pgp_num than the
# others since they are not expected to contain as much data
light = [
'.rgw.control',
'.rgw.data.root',
'.rgw.gc',
'.rgw.log',
'.rgw.intent-log',
'.rgw.meta',
'.rgw.usage',
'.rgw.users.keys',
'.rgw.users.email',
'.rgw.users.swift',
'.rgw.users.uid',
'.rgw.buckets.extra',
'.rgw.buckets.index',
]
pg_num = config('rgw-lightweight-pool-pg-num')
for pool in light:
_add_light_pool(rq, pool, pg_num, prefix)
_add_light_pool(rq, '.rgw.root', pg_num)
if config('restrict-ceph-pools'):
rq.add_op_request_access_to_group(name="objects",
permission='rwx',
key_name='radosgw.gateway')
return rq