Sync charms.ceph to get code cleanup changes
Also had to fix some imports due to changes implemented as part of the cleanup. Change-Id: I64de0ad077eaaf8ca6ac0c575c4ae7f19bccf8ee
This commit is contained in:
parent
a4385c1aeb
commit
f0042a9a7f
@ -27,7 +27,7 @@ from charmhelpers.core.hookenv import (
|
|||||||
action_fail,
|
action_fail,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ceph import get_local_osd_ids
|
from ceph.utils import get_local_osd_ids
|
||||||
from ceph_hooks import assess_status
|
from ceph_hooks import assess_status
|
||||||
|
|
||||||
from utils import (
|
from utils import (
|
||||||
|
@ -19,8 +19,8 @@ import sys
|
|||||||
import socket
|
import socket
|
||||||
|
|
||||||
sys.path.append('lib')
|
sys.path.append('lib')
|
||||||
import ceph
|
import ceph.utils as ceph
|
||||||
from ceph.ceph_broker import (
|
from ceph.broker import (
|
||||||
process_requests
|
process_requests
|
||||||
)
|
)
|
||||||
|
|
||||||
|
2157
lib/ceph/__init__.py
2157
lib/ceph/__init__.py
File diff suppressed because it is too large
Load Diff
@ -1,5 +1,3 @@
|
|||||||
#!/usr/bin/python
|
|
||||||
#
|
|
||||||
# Copyright 2016 Canonical Ltd
|
# Copyright 2016 Canonical Ltd
|
||||||
#
|
#
|
||||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
@ -16,19 +14,21 @@
|
|||||||
|
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
|
from ceph.utils import (
|
||||||
|
get_cephfs,
|
||||||
|
get_osd_weight
|
||||||
|
)
|
||||||
|
from ceph.crush_utils import Crushmap
|
||||||
|
|
||||||
from charmhelpers.core.hookenv import (
|
from charmhelpers.core.hookenv import (
|
||||||
log,
|
log,
|
||||||
DEBUG,
|
DEBUG,
|
||||||
INFO,
|
INFO,
|
||||||
ERROR,
|
ERROR,
|
||||||
)
|
)
|
||||||
from ceph import (
|
|
||||||
get_cephfs,
|
|
||||||
get_osd_weight
|
|
||||||
)
|
|
||||||
from ceph.ceph_helpers import Crushmap
|
|
||||||
from charmhelpers.contrib.storage.linux.ceph import (
|
from charmhelpers.contrib.storage.linux.ceph import (
|
||||||
create_erasure_profile,
|
create_erasure_profile,
|
||||||
delete_pool,
|
delete_pool,
|
||||||
@ -112,6 +112,9 @@ def process_requests(reqs):
|
|||||||
|
|
||||||
This is a versioned api. API version must be supplied by the client making
|
This is a versioned api. API version must be supplied by the client making
|
||||||
the request.
|
the request.
|
||||||
|
|
||||||
|
:param reqs: dict of request parameters.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
"""
|
"""
|
||||||
request_id = reqs.get('request-id')
|
request_id = reqs.get('request-id')
|
||||||
try:
|
try:
|
||||||
@ -140,6 +143,12 @@ def process_requests(reqs):
|
|||||||
|
|
||||||
|
|
||||||
def handle_create_erasure_profile(request, service):
|
def handle_create_erasure_profile(request, service):
|
||||||
|
"""Create an erasure profile.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
# "local" | "shec" or it defaults to "jerasure"
|
# "local" | "shec" or it defaults to "jerasure"
|
||||||
erasure_type = request.get('erasure-type')
|
erasure_type = request.get('erasure-type')
|
||||||
# "host" | "rack" or it defaults to "host" # Any valid Ceph bucket
|
# "host" | "rack" or it defaults to "host" # Any valid Ceph bucket
|
||||||
@ -160,10 +169,9 @@ def handle_create_erasure_profile(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_add_permissions_to_key(request, service):
|
def handle_add_permissions_to_key(request, service):
|
||||||
"""
|
"""Groups are defined by the key cephx.groups.(namespace-)?-(name). This
|
||||||
Groups are defined by the key cephx.groups.(namespace-)?-(name). This key
|
key will contain a dict serialized to JSON with data about the group,
|
||||||
will contain a dict serialized to JSON with data about the group, including
|
including pools and members.
|
||||||
pools and members.
|
|
||||||
|
|
||||||
A group can optionally have a namespace defined that will be used to
|
A group can optionally have a namespace defined that will be used to
|
||||||
further restrict pool access.
|
further restrict pool access.
|
||||||
@ -238,8 +246,7 @@ def pool_permission_list_for_service(service):
|
|||||||
|
|
||||||
|
|
||||||
def get_service_groups(service, namespace=None):
|
def get_service_groups(service, namespace=None):
|
||||||
"""
|
"""Services are objects stored with some metadata, they look like (for a
|
||||||
Services are objects stored with some metadata, they look like (for a
|
|
||||||
service named "nova"):
|
service named "nova"):
|
||||||
{
|
{
|
||||||
group_names: {'rwx': ['images']},
|
group_names: {'rwx': ['images']},
|
||||||
@ -272,7 +279,7 @@ def get_service_groups(service, namespace=None):
|
|||||||
|
|
||||||
|
|
||||||
def _build_service_groups(service, namespace=None):
|
def _build_service_groups(service, namespace=None):
|
||||||
'''Rebuild the 'groups' dict for a service group
|
"""Rebuild the 'groups' dict for a service group
|
||||||
|
|
||||||
:returns: dict: dictionary keyed by group name of the following
|
:returns: dict: dictionary keyed by group name of the following
|
||||||
format:
|
format:
|
||||||
@ -287,7 +294,7 @@ def _build_service_groups(service, namespace=None):
|
|||||||
services: ['nova']
|
services: ['nova']
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
'''
|
"""
|
||||||
all_groups = {}
|
all_groups = {}
|
||||||
for _, groups in service['group_names'].items():
|
for _, groups in service['group_names'].items():
|
||||||
for group in groups:
|
for group in groups:
|
||||||
@ -299,8 +306,7 @@ def _build_service_groups(service, namespace=None):
|
|||||||
|
|
||||||
|
|
||||||
def get_group(group_name):
|
def get_group(group_name):
|
||||||
"""
|
"""A group is a structure to hold data about a named group, structured as:
|
||||||
A group is a structure to hold data about a named group, structured as:
|
|
||||||
{
|
{
|
||||||
pools: ['glance'],
|
pools: ['glance'],
|
||||||
services: ['nova']
|
services: ['nova']
|
||||||
@ -344,6 +350,12 @@ def get_group_key(group_name):
|
|||||||
|
|
||||||
|
|
||||||
def handle_erasure_pool(request, service):
|
def handle_erasure_pool(request, service):
|
||||||
|
"""Create a new erasure coded pool.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params.
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0.
|
||||||
|
"""
|
||||||
pool_name = request.get('name')
|
pool_name = request.get('name')
|
||||||
erasure_profile = request.get('erasure-profile')
|
erasure_profile = request.get('erasure-profile')
|
||||||
quota = request.get('max-bytes')
|
quota = request.get('max-bytes')
|
||||||
@ -390,6 +402,12 @@ def handle_erasure_pool(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_replicated_pool(request, service):
|
def handle_replicated_pool(request, service):
|
||||||
|
"""Create a new replicated pool.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params.
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0.
|
||||||
|
"""
|
||||||
pool_name = request.get('name')
|
pool_name = request.get('name')
|
||||||
replicas = request.get('replicas')
|
replicas = request.get('replicas')
|
||||||
quota = request.get('max-bytes')
|
quota = request.get('max-bytes')
|
||||||
@ -441,6 +459,13 @@ def handle_replicated_pool(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_create_cache_tier(request, service):
|
def handle_create_cache_tier(request, service):
|
||||||
|
"""Create a cache tier on a cold pool. Modes supported are
|
||||||
|
"writeback" and "readonly".
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
# mode = "writeback" | "readonly"
|
# mode = "writeback" | "readonly"
|
||||||
storage_pool = request.get('cold-pool')
|
storage_pool = request.get('cold-pool')
|
||||||
cache_pool = request.get('hot-pool')
|
cache_pool = request.get('hot-pool')
|
||||||
@ -462,6 +487,12 @@ def handle_create_cache_tier(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_remove_cache_tier(request, service):
|
def handle_remove_cache_tier(request, service):
|
||||||
|
"""Remove a cache tier from the cold pool.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
storage_pool = request.get('cold-pool')
|
storage_pool = request.get('cold-pool')
|
||||||
cache_pool = request.get('hot-pool')
|
cache_pool = request.get('hot-pool')
|
||||||
# cache and storage pool must exist first
|
# cache and storage pool must exist first
|
||||||
@ -477,6 +508,12 @@ def handle_remove_cache_tier(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_set_pool_value(request, service):
|
def handle_set_pool_value(request, service):
|
||||||
|
"""Sets an arbitrary pool value.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
# Set arbitrary pool values
|
# Set arbitrary pool values
|
||||||
params = {'pool': request.get('name'),
|
params = {'pool': request.get('name'),
|
||||||
'key': request.get('key'),
|
'key': request.get('key'),
|
||||||
@ -501,6 +538,12 @@ def handle_set_pool_value(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_rgw_regionmap_update(request, service):
|
def handle_rgw_regionmap_update(request, service):
|
||||||
|
"""Change the radosgw region map.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
name = request.get('client-name')
|
name = request.get('client-name')
|
||||||
if not name:
|
if not name:
|
||||||
msg = "Missing rgw-region or client-name params"
|
msg = "Missing rgw-region or client-name params"
|
||||||
@ -516,6 +559,12 @@ def handle_rgw_regionmap_update(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_rgw_regionmap_default(request, service):
|
def handle_rgw_regionmap_default(request, service):
|
||||||
|
"""Create a radosgw region map.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
region = request.get('rgw-region')
|
region = request.get('rgw-region')
|
||||||
name = request.get('client-name')
|
name = request.get('client-name')
|
||||||
if not region or not name:
|
if not region or not name:
|
||||||
@ -537,6 +586,12 @@ def handle_rgw_regionmap_default(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_rgw_zone_set(request, service):
|
def handle_rgw_zone_set(request, service):
|
||||||
|
"""Create a radosgw zone.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
json_file = request.get('zone-json')
|
json_file = request.get('zone-json')
|
||||||
name = request.get('client-name')
|
name = request.get('client-name')
|
||||||
region_name = request.get('region-name')
|
region_name = request.get('region-name')
|
||||||
@ -567,6 +622,12 @@ def handle_rgw_zone_set(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_put_osd_in_bucket(request, service):
|
def handle_put_osd_in_bucket(request, service):
|
||||||
|
"""Move an osd into a specified crush bucket.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
osd_id = request.get('osd')
|
osd_id = request.get('osd')
|
||||||
target_bucket = request.get('bucket')
|
target_bucket = request.get('bucket')
|
||||||
if not osd_id or not target_bucket:
|
if not osd_id or not target_bucket:
|
||||||
@ -597,6 +658,12 @@ def handle_put_osd_in_bucket(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_rgw_create_user(request, service):
|
def handle_rgw_create_user(request, service):
|
||||||
|
"""Create a new rados gateway user.
|
||||||
|
|
||||||
|
:param request: dict of request operations and params
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
user_id = request.get('rgw-uid')
|
user_id = request.get('rgw-uid')
|
||||||
display_name = request.get('display-name')
|
display_name = request.get('display-name')
|
||||||
name = request.get('client-name')
|
name = request.get('client-name')
|
||||||
@ -630,11 +697,11 @@ def handle_rgw_create_user(request, service):
|
|||||||
|
|
||||||
|
|
||||||
def handle_create_cephfs(request, service):
|
def handle_create_cephfs(request, service):
|
||||||
"""
|
"""Create a new cephfs.
|
||||||
Create a new cephfs.
|
|
||||||
:param request: The broker request
|
:param request: The broker request
|
||||||
:param service: The cephx user to run this command under
|
:param service: The ceph client to run the command under.
|
||||||
:return:
|
:returns: dict. exit-code and reason if not 0
|
||||||
"""
|
"""
|
||||||
cephfs_name = request.get('mds_name')
|
cephfs_name = request.get('mds_name')
|
||||||
data_pool = request.get('data_pool')
|
data_pool = request.get('data_pool')
|
||||||
@ -678,6 +745,12 @@ def handle_create_cephfs(request, service):
|
|||||||
|
|
||||||
def handle_rgw_region_set(request, service):
|
def handle_rgw_region_set(request, service):
|
||||||
# radosgw-admin region set --infile us.json --name client.radosgw.us-east-1
|
# radosgw-admin region set --infile us.json --name client.radosgw.us-east-1
|
||||||
|
"""Set the rados gateway region.
|
||||||
|
|
||||||
|
:param request: dict. The broker request.
|
||||||
|
:param service: The ceph client to run the command under.
|
||||||
|
:returns: dict. exit-code and reason if not 0
|
||||||
|
"""
|
||||||
json_file = request.get('region-json')
|
json_file = request.get('region-json')
|
||||||
name = request.get('client-name')
|
name = request.get('client-name')
|
||||||
region_name = request.get('region-name')
|
region_name = request.get('region-name')
|
File diff suppressed because it is too large
Load Diff
149
lib/ceph/crush_utils.py
Normal file
149
lib/ceph/crush_utils.py
Normal file
@ -0,0 +1,149 @@
|
|||||||
|
# Copyright 2014 Canonical Limited.
|
||||||
|
#
|
||||||
|
# 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 re
|
||||||
|
|
||||||
|
from subprocess import check_output, CalledProcessError
|
||||||
|
|
||||||
|
from charmhelpers.core.hookenv import (
|
||||||
|
log,
|
||||||
|
ERROR,
|
||||||
|
)
|
||||||
|
|
||||||
|
CRUSH_BUCKET = """root {name} {{
|
||||||
|
id {id} # do not change unnecessarily
|
||||||
|
# weight 0.000
|
||||||
|
alg straw
|
||||||
|
hash 0 # rjenkins1
|
||||||
|
}}
|
||||||
|
|
||||||
|
rule {name} {{
|
||||||
|
ruleset 0
|
||||||
|
type replicated
|
||||||
|
min_size 1
|
||||||
|
max_size 10
|
||||||
|
step take {name}
|
||||||
|
step chooseleaf firstn 0 type host
|
||||||
|
step emit
|
||||||
|
}}"""
|
||||||
|
|
||||||
|
# This regular expression looks for a string like:
|
||||||
|
# root NAME {
|
||||||
|
# id NUMBER
|
||||||
|
# so that we can extract NAME and ID from the crushmap
|
||||||
|
CRUSHMAP_BUCKETS_RE = re.compile(r"root\s+(.+)\s+\{\s*id\s+(-?\d+)")
|
||||||
|
|
||||||
|
# This regular expression looks for ID strings in the crushmap like:
|
||||||
|
# id NUMBER
|
||||||
|
# so that we can extract the IDs from a crushmap
|
||||||
|
CRUSHMAP_ID_RE = re.compile(r"id\s+(-?\d+)")
|
||||||
|
|
||||||
|
|
||||||
|
class Crushmap(object):
|
||||||
|
"""An object oriented approach to Ceph crushmap management."""
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self._crushmap = self.load_crushmap()
|
||||||
|
roots = re.findall(CRUSHMAP_BUCKETS_RE, self._crushmap)
|
||||||
|
buckets = []
|
||||||
|
ids = list(map(
|
||||||
|
lambda x: int(x),
|
||||||
|
re.findall(CRUSHMAP_ID_RE, self._crushmap)))
|
||||||
|
ids.sort()
|
||||||
|
if roots != []:
|
||||||
|
for root in roots:
|
||||||
|
buckets.append(CRUSHBucket(root[0], root[1], True))
|
||||||
|
|
||||||
|
self._buckets = buckets
|
||||||
|
if ids != []:
|
||||||
|
self._ids = ids
|
||||||
|
else:
|
||||||
|
self._ids = [0]
|
||||||
|
|
||||||
|
def load_crushmap(self):
|
||||||
|
try:
|
||||||
|
crush = check_output(['ceph', 'osd', 'getcrushmap'])
|
||||||
|
return check_output(['crushtool', '-d', '-'], stdin=crush.stdout)
|
||||||
|
except CalledProcessError as e:
|
||||||
|
log("Error occured while loading and decompiling CRUSH map:"
|
||||||
|
"{}".format(e), ERROR)
|
||||||
|
raise "Failed to read CRUSH map"
|
||||||
|
|
||||||
|
def ensure_bucket_is_present(self, bucket_name):
|
||||||
|
if bucket_name not in [bucket.name for bucket in self.buckets()]:
|
||||||
|
self.add_bucket(bucket_name)
|
||||||
|
self.save()
|
||||||
|
|
||||||
|
def buckets(self):
|
||||||
|
"""Return a list of buckets that are in the Crushmap."""
|
||||||
|
return self._buckets
|
||||||
|
|
||||||
|
def add_bucket(self, bucket_name):
|
||||||
|
"""Add a named bucket to Ceph"""
|
||||||
|
new_id = min(self._ids) - 1
|
||||||
|
self._ids.append(new_id)
|
||||||
|
self._buckets.append(CRUSHBucket(bucket_name, new_id))
|
||||||
|
|
||||||
|
def save(self):
|
||||||
|
"""Persist Crushmap to Ceph"""
|
||||||
|
try:
|
||||||
|
crushmap = self.build_crushmap()
|
||||||
|
compiled = check_output(['crushtool', '-c', '/dev/stdin', '-o',
|
||||||
|
'/dev/stdout'], stdin=crushmap)
|
||||||
|
ceph_output = check_output(['ceph', 'osd', 'setcrushmap', '-i',
|
||||||
|
'/dev/stdin'], stdin=compiled)
|
||||||
|
return ceph_output
|
||||||
|
except CalledProcessError as e:
|
||||||
|
log("save error: {}".format(e))
|
||||||
|
raise "Failed to save CRUSH map."
|
||||||
|
|
||||||
|
def build_crushmap(self):
|
||||||
|
"""Modifies the current CRUSH map to include the new buckets"""
|
||||||
|
tmp_crushmap = self._crushmap
|
||||||
|
for bucket in self._buckets:
|
||||||
|
if not bucket.default:
|
||||||
|
tmp_crushmap = "{}\n\n{}".format(
|
||||||
|
tmp_crushmap,
|
||||||
|
Crushmap.bucket_string(bucket.name, bucket.id))
|
||||||
|
|
||||||
|
return tmp_crushmap
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def bucket_string(name, id):
|
||||||
|
return CRUSH_BUCKET.format(name=name, id=id)
|
||||||
|
|
||||||
|
|
||||||
|
class CRUSHBucket(object):
|
||||||
|
"""CRUSH bucket description object."""
|
||||||
|
|
||||||
|
def __init__(self, name, id, default=False):
|
||||||
|
self.name = name
|
||||||
|
self.id = int(id)
|
||||||
|
self.default = default
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "Bucket {{Name: {name}, ID: {id}}}".format(
|
||||||
|
name=self.name, id=self.id)
|
||||||
|
|
||||||
|
def __eq__(self, other):
|
||||||
|
"""Override the default Equals behavior"""
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
return self.__dict__ == other.__dict__
|
||||||
|
return NotImplemented
|
||||||
|
|
||||||
|
def __ne__(self, other):
|
||||||
|
"""Define a non-equality test"""
|
||||||
|
if isinstance(other, self.__class__):
|
||||||
|
return not self.__eq__(other)
|
||||||
|
return NotImplemented
|
2199
lib/ceph/utils.py
Normal file
2199
lib/ceph/utils.py
Normal file
File diff suppressed because it is too large
Load Diff
85
lib/setup.py
85
lib/setup.py
@ -1,85 +0,0 @@
|
|||||||
# -*- coding: utf-8 -*-
|
|
||||||
from __future__ import print_function
|
|
||||||
|
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
from setuptools import setup, find_packages
|
|
||||||
from setuptools.command.test import test as TestCommand
|
|
||||||
|
|
||||||
version = "0.0.1.dev1"
|
|
||||||
install_require = [
|
|
||||||
]
|
|
||||||
|
|
||||||
tests_require = [
|
|
||||||
'tox >= 2.3.1',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
class Tox(TestCommand):
|
|
||||||
|
|
||||||
user_options = [('tox-args=', 'a', "Arguments to pass to tox")]
|
|
||||||
|
|
||||||
def initialize_options(self):
|
|
||||||
TestCommand.initialize_options(self)
|
|
||||||
self.tox_args = None
|
|
||||||
|
|
||||||
def finalize_options(self):
|
|
||||||
TestCommand.finalize_options(self)
|
|
||||||
self.test_args = []
|
|
||||||
self.test_suite = True
|
|
||||||
|
|
||||||
def run_tests(self):
|
|
||||||
# import here, cause outside the eggs aren't loaded
|
|
||||||
import tox
|
|
||||||
import shlex
|
|
||||||
args = self.tox_args
|
|
||||||
# remove the 'test' arg from argv as tox passes it to ostestr which
|
|
||||||
# breaks it.
|
|
||||||
sys.argv.pop()
|
|
||||||
if args:
|
|
||||||
args = shlex.split(self.tox_args)
|
|
||||||
errno = tox.cmdline(args=args)
|
|
||||||
sys.exit(errno)
|
|
||||||
|
|
||||||
|
|
||||||
if sys.argv[-1] == 'publish':
|
|
||||||
os.system("python setup.py sdist upload")
|
|
||||||
os.system("python setup.py bdist_wheel upload")
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
if sys.argv[-1] == 'tag':
|
|
||||||
os.system("git tag -a %s -m 'version %s'" % (version, version))
|
|
||||||
os.system("git push --tags")
|
|
||||||
sys.exit()
|
|
||||||
|
|
||||||
|
|
||||||
setup(
|
|
||||||
name='charms.ceph',
|
|
||||||
version=version,
|
|
||||||
description='Provide base module for ceph charms.',
|
|
||||||
classifiers=[
|
|
||||||
"Development Status :: 2 - Pre-Alpha",
|
|
||||||
"Intended Audience :: Developers",
|
|
||||||
"Topic :: System",
|
|
||||||
"Topic :: System :: Installation/Setup",
|
|
||||||
"Topic :: System :: Software Distribution",
|
|
||||||
"Programming Language :: Python :: 2",
|
|
||||||
"Programming Language :: Python :: 2.7",
|
|
||||||
"Programming Language :: Python :: 3",
|
|
||||||
"Programming Language :: Python :: 3.5",
|
|
||||||
"License :: OSI Approved :: Apache Software License",
|
|
||||||
],
|
|
||||||
url='https://github.com/openstack/charms.ceph',
|
|
||||||
author='OpenStack Charmers',
|
|
||||||
author_email='openstack-dev@lists.openstack.org',
|
|
||||||
license='Apache-2.0: http://www.apache.org/licenses/LICENSE-2.0',
|
|
||||||
packages=find_packages(exclude=["unit_tests"]),
|
|
||||||
zip_safe=False,
|
|
||||||
cmdclass={'test': Tox},
|
|
||||||
install_requires=install_require,
|
|
||||||
extras_require={
|
|
||||||
'testing': tests_require,
|
|
||||||
},
|
|
||||||
tests_require=tests_require,
|
|
||||||
)
|
|
@ -20,13 +20,13 @@ from mock import (
|
|||||||
patch,
|
patch,
|
||||||
)
|
)
|
||||||
|
|
||||||
from ceph import ceph_broker
|
from ceph import broker
|
||||||
|
|
||||||
|
|
||||||
class TestCephOps(unittest.TestCase):
|
class TestCephOps(unittest.TestCase):
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'create_erasure_profile')
|
@patch.object(broker, 'create_erasure_profile')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_create_erasure_profile(self, mock_create_erasure):
|
def test_create_erasure_profile(self, mock_create_erasure):
|
||||||
req = json.dumps({'api-version': 1,
|
req = json.dumps({'api-version': 1,
|
||||||
'ops': [{
|
'ops': [{
|
||||||
@ -37,7 +37,7 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'k': 3,
|
'k': 3,
|
||||||
'm': 2,
|
'm': 2,
|
||||||
}]})
|
}]})
|
||||||
rc = ceph_broker.process_requests(req)
|
rc = broker.process_requests(req)
|
||||||
mock_create_erasure.assert_called_with(service='admin',
|
mock_create_erasure.assert_called_with(service='admin',
|
||||||
profile_name='foo',
|
profile_name='foo',
|
||||||
coding_chunks=2,
|
coding_chunks=2,
|
||||||
@ -47,9 +47,9 @@ class TestCephOps(unittest.TestCase):
|
|||||||
erasure_plugin_name='jerasure')
|
erasure_plugin_name='jerasure')
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'pool_exists')
|
@patch.object(broker, 'pool_exists')
|
||||||
@patch.object(ceph_broker, 'ReplicatedPool')
|
@patch.object(broker, 'ReplicatedPool')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_process_requests_create_replicated_pool(self,
|
def test_process_requests_create_replicated_pool(self,
|
||||||
mock_replicated_pool,
|
mock_replicated_pool,
|
||||||
mock_pool_exists):
|
mock_pool_exists):
|
||||||
@ -61,15 +61,15 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'name': 'foo',
|
'name': 'foo',
|
||||||
'replicas': 3
|
'replicas': 3
|
||||||
}]})
|
}]})
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_pool_exists.assert_called_with(service='admin', name='foo')
|
mock_pool_exists.assert_called_with(service='admin', name='foo')
|
||||||
calls = [call(name=u'foo', service='admin', replicas=3)]
|
calls = [call(name=u'foo', service='admin', replicas=3)]
|
||||||
mock_replicated_pool.assert_has_calls(calls)
|
mock_replicated_pool.assert_has_calls(calls)
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'pool_exists')
|
@patch.object(broker, 'pool_exists')
|
||||||
@patch.object(ceph_broker, 'ReplicatedPool')
|
@patch.object(broker, 'ReplicatedPool')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_process_requests_replicated_pool_weight(self,
|
def test_process_requests_replicated_pool_weight(self,
|
||||||
mock_replicated_pool,
|
mock_replicated_pool,
|
||||||
mock_pool_exists):
|
mock_pool_exists):
|
||||||
@ -82,15 +82,15 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'weight': 40.0,
|
'weight': 40.0,
|
||||||
'replicas': 3
|
'replicas': 3
|
||||||
}]})
|
}]})
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_pool_exists.assert_called_with(service='admin', name='foo')
|
mock_pool_exists.assert_called_with(service='admin', name='foo')
|
||||||
calls = [call(name=u'foo', service='admin', replicas=3,
|
calls = [call(name=u'foo', service='admin', replicas=3,
|
||||||
percent_data=40.0)]
|
percent_data=40.0)]
|
||||||
mock_replicated_pool.assert_has_calls(calls)
|
mock_replicated_pool.assert_has_calls(calls)
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'delete_pool')
|
@patch.object(broker, 'delete_pool')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_process_requests_delete_pool(self,
|
def test_process_requests_delete_pool(self,
|
||||||
mock_delete_pool):
|
mock_delete_pool):
|
||||||
reqs = json.dumps({'api-version': 1,
|
reqs = json.dumps({'api-version': 1,
|
||||||
@ -99,14 +99,14 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'name': 'foo',
|
'name': 'foo',
|
||||||
}]})
|
}]})
|
||||||
mock_delete_pool.return_value = {'exit-code': 0}
|
mock_delete_pool.return_value = {'exit-code': 0}
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_delete_pool.assert_called_with(service='admin', name='foo')
|
mock_delete_pool.assert_called_with(service='admin', name='foo')
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'pool_exists')
|
@patch.object(broker, 'pool_exists')
|
||||||
@patch.object(ceph_broker.ErasurePool, 'create')
|
@patch.object(broker.ErasurePool, 'create')
|
||||||
@patch.object(ceph_broker, 'erasure_profile_exists')
|
@patch.object(broker, 'erasure_profile_exists')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_process_requests_create_erasure_pool(self, mock_profile_exists,
|
def test_process_requests_create_erasure_pool(self, mock_profile_exists,
|
||||||
mock_erasure_pool,
|
mock_erasure_pool,
|
||||||
mock_pool_exists):
|
mock_pool_exists):
|
||||||
@ -118,15 +118,15 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'name': 'foo',
|
'name': 'foo',
|
||||||
'erasure-profile': 'default'
|
'erasure-profile': 'default'
|
||||||
}]})
|
}]})
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_profile_exists.assert_called_with(service='admin', name='default')
|
mock_profile_exists.assert_called_with(service='admin', name='default')
|
||||||
mock_pool_exists.assert_called_with(service='admin', name='foo')
|
mock_pool_exists.assert_called_with(service='admin', name='foo')
|
||||||
mock_erasure_pool.assert_called_with()
|
mock_erasure_pool.assert_called_with()
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'pool_exists')
|
@patch.object(broker, 'pool_exists')
|
||||||
@patch.object(ceph_broker.Pool, 'add_cache_tier')
|
@patch.object(broker.Pool, 'add_cache_tier')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_process_requests_create_cache_tier(self, mock_pool,
|
def test_process_requests_create_cache_tier(self, mock_pool,
|
||||||
mock_pool_exists):
|
mock_pool_exists):
|
||||||
mock_pool_exists.return_value = True
|
mock_pool_exists.return_value = True
|
||||||
@ -138,16 +138,16 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'mode': 'writeback',
|
'mode': 'writeback',
|
||||||
'erasure-profile': 'default'
|
'erasure-profile': 'default'
|
||||||
}]})
|
}]})
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_pool_exists.assert_any_call(service='admin', name='foo')
|
mock_pool_exists.assert_any_call(service='admin', name='foo')
|
||||||
mock_pool_exists.assert_any_call(service='admin', name='foo-ssd')
|
mock_pool_exists.assert_any_call(service='admin', name='foo-ssd')
|
||||||
|
|
||||||
mock_pool.assert_called_with(cache_pool='foo-ssd', mode='writeback')
|
mock_pool.assert_called_with(cache_pool='foo-ssd', mode='writeback')
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'pool_exists')
|
@patch.object(broker, 'pool_exists')
|
||||||
@patch.object(ceph_broker.Pool, 'remove_cache_tier')
|
@patch.object(broker.Pool, 'remove_cache_tier')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_process_requests_remove_cache_tier(self, mock_pool,
|
def test_process_requests_remove_cache_tier(self, mock_pool,
|
||||||
mock_pool_exists):
|
mock_pool_exists):
|
||||||
mock_pool_exists.return_value = True
|
mock_pool_exists.return_value = True
|
||||||
@ -156,14 +156,14 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'op': 'remove-cache-tier',
|
'op': 'remove-cache-tier',
|
||||||
'hot-pool': 'foo-ssd',
|
'hot-pool': 'foo-ssd',
|
||||||
}]})
|
}]})
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_pool_exists.assert_any_call(service='admin', name='foo-ssd')
|
mock_pool_exists.assert_any_call(service='admin', name='foo-ssd')
|
||||||
|
|
||||||
mock_pool.assert_called_with(cache_pool='foo-ssd')
|
mock_pool.assert_called_with(cache_pool='foo-ssd')
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'snapshot_pool')
|
@patch.object(broker, 'snapshot_pool')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_snapshot_pool(self, mock_snapshot_pool):
|
def test_snapshot_pool(self, mock_snapshot_pool):
|
||||||
reqs = json.dumps({'api-version': 1,
|
reqs = json.dumps({'api-version': 1,
|
||||||
'ops': [{
|
'ops': [{
|
||||||
@ -172,14 +172,14 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'snapshot-name': 'foo-snap1',
|
'snapshot-name': 'foo-snap1',
|
||||||
}]})
|
}]})
|
||||||
mock_snapshot_pool.return_value = {'exit-code': 0}
|
mock_snapshot_pool.return_value = {'exit-code': 0}
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_snapshot_pool.assert_called_with(service='admin',
|
mock_snapshot_pool.assert_called_with(service='admin',
|
||||||
pool_name='foo',
|
pool_name='foo',
|
||||||
snapshot_name='foo-snap1')
|
snapshot_name='foo-snap1')
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'rename_pool')
|
@patch.object(broker, 'rename_pool')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_rename_pool(self, mock_rename_pool):
|
def test_rename_pool(self, mock_rename_pool):
|
||||||
reqs = json.dumps({'api-version': 1,
|
reqs = json.dumps({'api-version': 1,
|
||||||
'ops': [{
|
'ops': [{
|
||||||
@ -188,14 +188,14 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'new-name': 'foo2',
|
'new-name': 'foo2',
|
||||||
}]})
|
}]})
|
||||||
mock_rename_pool.return_value = {'exit-code': 0}
|
mock_rename_pool.return_value = {'exit-code': 0}
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_rename_pool.assert_called_with(service='admin',
|
mock_rename_pool.assert_called_with(service='admin',
|
||||||
old_name='foo',
|
old_name='foo',
|
||||||
new_name='foo2')
|
new_name='foo2')
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'remove_pool_snapshot')
|
@patch.object(broker, 'remove_pool_snapshot')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_remove_pool_snapshot(self, mock_snapshot_pool):
|
def test_remove_pool_snapshot(self, mock_snapshot_pool):
|
||||||
reqs = json.dumps({'api-version': 1,
|
reqs = json.dumps({'api-version': 1,
|
||||||
'ops': [{
|
'ops': [{
|
||||||
@ -204,14 +204,14 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'snapshot-name': 'foo-snap1',
|
'snapshot-name': 'foo-snap1',
|
||||||
}]})
|
}]})
|
||||||
mock_snapshot_pool.return_value = {'exit-code': 0}
|
mock_snapshot_pool.return_value = {'exit-code': 0}
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_snapshot_pool.assert_called_with(service='admin',
|
mock_snapshot_pool.assert_called_with(service='admin',
|
||||||
pool_name='foo',
|
pool_name='foo',
|
||||||
snapshot_name='foo-snap1')
|
snapshot_name='foo-snap1')
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'pool_set')
|
@patch.object(broker, 'pool_set')
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_set_pool_value(self, mock_set_pool):
|
def test_set_pool_value(self, mock_set_pool):
|
||||||
reqs = json.dumps({'api-version': 1,
|
reqs = json.dumps({'api-version': 1,
|
||||||
'ops': [{
|
'ops': [{
|
||||||
@ -221,14 +221,14 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'value': 3,
|
'value': 3,
|
||||||
}]})
|
}]})
|
||||||
mock_set_pool.return_value = {'exit-code': 0}
|
mock_set_pool.return_value = {'exit-code': 0}
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
mock_set_pool.assert_called_with(service='admin',
|
mock_set_pool.assert_called_with(service='admin',
|
||||||
pool_name='foo',
|
pool_name='foo',
|
||||||
key='size',
|
key='size',
|
||||||
value=3)
|
value=3)
|
||||||
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
self.assertEqual(json.loads(rc), {'exit-code': 0})
|
||||||
|
|
||||||
@patch.object(ceph_broker, 'log', lambda *args, **kwargs: None)
|
@patch.object(broker, 'log', lambda *args, **kwargs: None)
|
||||||
def test_set_invalid_pool_value(self):
|
def test_set_invalid_pool_value(self):
|
||||||
reqs = json.dumps({'api-version': 1,
|
reqs = json.dumps({'api-version': 1,
|
||||||
'ops': [{
|
'ops': [{
|
||||||
@ -237,5 +237,5 @@ class TestCephOps(unittest.TestCase):
|
|||||||
'key': 'size',
|
'key': 'size',
|
||||||
'value': 'abc',
|
'value': 'abc',
|
||||||
}]})
|
}]})
|
||||||
rc = ceph_broker.process_requests(reqs)
|
rc = broker.process_requests(reqs)
|
||||||
self.assertEqual(json.loads(rc)['exit-code'], 1)
|
self.assertEqual(json.loads(rc)['exit-code'], 1)
|
||||||
|
Loading…
Reference in New Issue
Block a user