From: Samarah Date: Thu, 23 Mar 2023 16:46:05 +0000 (-0400) Subject: QA: Add D4N teuthology suite X-Git-Tag: v19.0.0~1055^2 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=refs%2Fpull%2F48879%2Fhead;p=ceph.git QA: Add D4N teuthology suite Signed-off-by: Samarah --- diff --git a/qa/suites/rgw/d4n/+ b/qa/suites/rgw/d4n/+ new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/qa/suites/rgw/d4n/.qa b/qa/suites/rgw/d4n/.qa new file mode 120000 index 000000000000..a602a0353e75 --- /dev/null +++ b/qa/suites/rgw/d4n/.qa @@ -0,0 +1 @@ +../.qa/ \ No newline at end of file diff --git a/qa/suites/rgw/d4n/cluster.yaml b/qa/suites/rgw/d4n/cluster.yaml new file mode 100644 index 000000000000..f56f945cd327 --- /dev/null +++ b/qa/suites/rgw/d4n/cluster.yaml @@ -0,0 +1,2 @@ +roles: +- [mon.a, osd.0, osd.1, osd.2, mgr.0, client.0] diff --git a/qa/suites/rgw/d4n/overrides.yaml b/qa/suites/rgw/d4n/overrides.yaml new file mode 100644 index 000000000000..e04747488e33 --- /dev/null +++ b/qa/suites/rgw/d4n/overrides.yaml @@ -0,0 +1,10 @@ +overrides: + ceph: + conf: + client: + setuser: ceph + setgroup: ceph + debug rgw: 20 + rgw filter: d4n + rgw: + frontend: beast diff --git a/qa/suites/rgw/d4n/supported-random-distro$ b/qa/suites/rgw/d4n/supported-random-distro$ new file mode 120000 index 000000000000..0862b4457b37 --- /dev/null +++ b/qa/suites/rgw/d4n/supported-random-distro$ @@ -0,0 +1 @@ +.qa/distros/supported-random-distro$ \ No newline at end of file diff --git a/qa/suites/rgw/d4n/tasks/.qa b/qa/suites/rgw/d4n/tasks/.qa new file mode 120000 index 000000000000..a602a0353e75 --- /dev/null +++ b/qa/suites/rgw/d4n/tasks/.qa @@ -0,0 +1 @@ +../.qa/ \ No newline at end of file diff --git a/qa/suites/rgw/d4n/tasks/rgw_d4ntests.yaml b/qa/suites/rgw/d4n/tasks/rgw_d4ntests.yaml new file mode 100644 index 000000000000..893119e82d5d --- /dev/null +++ b/qa/suites/rgw/d4n/tasks/rgw_d4ntests.yaml @@ -0,0 +1,19 @@ +tasks: +- install: + # extra packages added for the rgw-d4ntests task + extra_system_packages: + deb: ['s3cmd', 'redis'] + rpm: ['s3cmd', 'redis'] +- ceph: +- rgw: [client.0] +- tox: [client.0] +- exec: + client.0: + - sudo chmod 0777 /var/lib/ceph + - sudo chmod 0777 /var/lib/ceph/radosgw +- d4ntests: + client.0: +- workunit: + clients: + client.0: + - rgw/run-d4n.sh diff --git a/qa/tasks/d4ntests.py b/qa/tasks/d4ntests.py new file mode 100644 index 000000000000..9a7b6a1f6d6a --- /dev/null +++ b/qa/tasks/d4ntests.py @@ -0,0 +1,152 @@ +import logging + +from teuthology import misc as teuthology +from teuthology.task import Task +from teuthology.orchestra import run +from teuthology.packaging import remove_package + +log = logging.getLogger(__name__) + +def get_toxvenv_dir(ctx): + return ctx.tox.venv_path + +def toxvenv_sh(ctx, remote, args, **kwargs): + activate = get_toxvenv_dir(ctx) + '/bin/activate' + return remote.sh(['source', activate, run.Raw('&&')] + args, **kwargs) + +display_name='Foo' +email='foo@foo.com' +access_key='test3' +secret_key='test3' + +class D4NTests(Task): + + def __init__(self, ctx, config): + super(D4NTests, self).__init__(ctx, config) + self.log = log + log.info('D4N Tests: __INIT__ ') + + clients = ['client.{id}'.format(id=id_) + for id_ in teuthology.all_roles_of_type(self.ctx.cluster, 'client')] + self.all_clients = [] + for client in clients: + if client in self.config: + self.all_clients.extend([client]) + if self.all_clients is None: + self.all_clients = 'client.0' + + self.user = {'s3main': 'tester'} + + def setup(self): + super(D4NTests, self).setup() + log.info('D4N Tests: SETUP') + + def begin(self): + super(D4NTests, self).begin() + log.info('D4N Tests: BEGIN') + + for (host, roles) in self.ctx.cluster.remotes.items(): + log.debug('D4N Tests: Cluster config is: {cfg}'.format(cfg=roles)) + log.debug('D4N Tests: Host is: {host}'.format(host=host)) + + self.create_user() + self.redis_startup() + + def end(self): + super(D4NTests, self).end() + log.info('D4N Tests: END') + + self.redis_shutdown() + + for client in self.all_clients: + self.remove_packages(client) + self.delete_user(client) + + def create_user(self): + log.info("D4N Tests: Creating S3 user...") + testdir = teuthology.get_testdir(self.ctx) + + for client in self.all_clients: + for user in list(self.user.items()): + s3_user_id = 's3main' + log.debug( + 'D4N Tests: Creating user {s3_user_id}'.format(s3_user_id=s3_user_id)) + cluster_name, daemon_type, client_id = teuthology.split_role( + client) + client_with_id = daemon_type + '.' + client_id + self.ctx.cluster.only(client).run( + args=[ + 'sudo', + 'adjust-ulimits', + 'ceph-coverage', + '{tdir}/archive/coverage'.format(tdir=testdir), + 'radosgw-admin', + '-n', client_with_id, + 'user', 'create', + '--uid', s3_user_id, + '--display-name', display_name, + '--access-key', access_key, + '--secret', secret_key, + '--email', email, + '--cluster', cluster_name, + ], + ) + + def redis_startup(self): + try: + for client in self.all_clients: + self.ctx.cluster.only(client).run( + args=[ + 'sudo', + 'redis-server', + '--daemonize', + 'yes' + ], + ) + + except Exception as err: + log.debug('D4N Tests: Error starting up a Redis server') + log.debug(err) + + def redis_shutdown(self): + try: + for client in self.all_clients: + self.ctx.cluster.only(client).run( + args=[ + 'sudo', + 'redis-cli', + 'shutdown', + ], + ) + + except Exception as err: + log.debug('D4N Tests: Error shutting down a Redis server') + log.debug(err) + + def remove_packages(self, client): + (remote,) = self.ctx.cluster.only(client).remotes.keys() + remove_package('s3cmd', remote) + remove_package('redis', remote) + + def delete_user(self, client): + log.info("D4N Tests: Deleting S3 user...") + testdir = teuthology.get_testdir(self.ctx) + + for user in self.user.items(): + s3_user_id = 's3main' + self.ctx.cluster.only(client).run( + args=[ + 'sudo', + 'adjust-ulimits', + 'ceph-coverage', + '{tdir}/archive/coverage'.format(tdir=testdir), + 'radosgw-admin', + '-n', client, + 'user', 'rm', + '--uid', s3_user_id, + '--purge-data', + '--cluster', 'ceph', + ], + ) + +task = D4NTests diff --git a/qa/tox.ini b/qa/tox.ini index 5b2149c4989b..234873c43386 100644 --- a/qa/tox.ini +++ b/qa/tox.ini @@ -18,6 +18,7 @@ basepython = python3 deps = mypy types-boto + types-redis types-requests types-jwt types-paramiko diff --git a/qa/workunits/rgw/run-d4n.sh b/qa/workunits/rgw/run-d4n.sh new file mode 100755 index 000000000000..ca2f65f6c769 --- /dev/null +++ b/qa/workunits/rgw/run-d4n.sh @@ -0,0 +1,16 @@ +#!/usr/bin/env bash +set -ex +mydir=`dirname $0` + +python3 -m venv $mydir +source $mydir/bin/activate +pip install pip --upgrade +pip install redis +pip install configobj +pip install boto3 + +# run test +$mydir/bin/python3 $mydir/test_rgw_d4n.py + +deactivate +echo OK. diff --git a/qa/workunits/rgw/test_rgw_d4n.py b/qa/workunits/rgw/test_rgw_d4n.py new file mode 100644 index 000000000000..c5ba5ec65688 --- /dev/null +++ b/qa/workunits/rgw/test_rgw_d4n.py @@ -0,0 +1,243 @@ +#!/usr/bin/python3 + +import logging as log +from configobj import ConfigObj +import boto3 +import redis +import subprocess +import json + +log.basicConfig(level=log.DEBUG) + +""" Constants """ +ACCESS_KEY = 'test3' +SECRET_KEY = 'test3' + +def exec_cmd(cmd): + log.debug("exec_cmd(%s)", cmd) + try: + proc = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, shell=True) + out, err = proc.communicate() + if proc.returncode == 0: + log.info('command succeeded') + if out is not None: log.info(out) + return out + else: + raise Exception("error: %s \nreturncode: %s" % (err, proc.returncode)) + except Exception as e: + log.error('command failed') + log.error(e) + return False + +def get_radosgw_endpoint(): + out = exec_cmd('sudo netstat -nltp | egrep "rados|valgr"') # short for radosgw/valgrind + x = out.decode('utf8').split(" ") + port = [i for i in x if ':' in i][0].split(':')[1] + log.info('radosgw port: %s' % port) + proto = "http" + hostname = '127.0.0.1' + + if port == '443': + proto = "https" + + endpoint = "%s://%s:%s" % (proto, hostname, port) + + log.info("radosgw endpoint is: %s", endpoint) + return endpoint, proto + +def create_s3cmd_config(path, proto): + """ + Creates a minimal config file for s3cmd + """ + log.info("Creating s3cmd config...") + + use_https_config = "False" + log.info("proto for s3cmd config is %s", proto) + if proto == "https": + use_https_config = "True" + + s3cmd_config = ConfigObj( + indent_type='', + infile={ + 'default': + { + 'host_bucket': 'no.way.in.hell', + 'use_https': use_https_config, + }, + } + ) + + f = open(path, 'wb') + s3cmd_config.write(f) + f.close() + log.info("s3cmd config written") + +def get_cmd_output(cmd_out): + out = cmd_out.decode('utf8') + out = out.strip('\n') + return out + +def test_directory_methods(r, client, obj): + test_txt = b'test' + + # setValue call + response_put = obj.put(Body=test_txt) + + assert(response_put.get('ResponseMetadata').get('HTTPStatusCode') == 200) + + data = r.hgetall('rgw-object:test.txt:directory') + + assert(data.get('key') == 'rgw-object:test.txt:directory') + assert(data.get('size') == '4') + assert(data.get('bucket_name') == 'bkt') + assert(data.get('obj_name') == 'test.txt') + assert(data.get('hosts') == '127.0.0.1:6379') + + # getValue call + response_get = obj.get() + + assert(response_get.get('ResponseMetadata').get('HTTPStatusCode') == 200) + + data = r.hgetall('rgw-object:test.txt:directory') + + assert(data.get('key') == 'rgw-object:test.txt:directory') + assert(data.get('size') == '4') + assert(data.get('bucket_name') == 'bkt') + assert(data.get('obj_name') == 'test.txt') + assert(data.get('hosts') == '127.0.0.1:6379') + + # delValue call + response_del = obj.delete() + + assert(response_del.get('ResponseMetadata').get('HTTPStatusCode') == 204) + assert(r.exists('rgw-object:test.txt:directory') == False) + + r.flushall() + +def test_cache_methods(r, client, obj): + test_txt = b'test' + + # setObject call + response_put = obj.put(Body=test_txt) + + assert(response_put.get('ResponseMetadata').get('HTTPStatusCode') == 200) + + data = r.hgetall('rgw-object:test.txt:cache') + output = subprocess.check_output(['radosgw-admin', 'object', 'stat', '--bucket=bkt', '--object=test.txt']) + attrs = json.loads(output.decode('latin-1')) + + assert((data.get(b'user.rgw.tail_tag')) == attrs.get('attrs').get('user.rgw.tail_tag').encode("latin-1") + b'\x00') + assert((data.get(b'user.rgw.pg_ver')) == attrs.get('attrs').get('user.rgw.pg_ver').encode("latin-1") + b'\x00\x00\x00\x00\x00\x00\x00') + assert((data.get(b'user.rgw.idtag')) == attrs.get('tag').encode("latin-1") + b'\x00') + assert((data.get(b'user.rgw.etag')) == attrs.get('etag').encode("latin-1")) + assert((data.get(b'user.rgw.x-amz-content-sha256')) == attrs.get('attrs').get('user.rgw.x-amz-content-sha256').encode("latin-1") + b'\x00') + assert((data.get(b'user.rgw.source_zone')) == attrs.get('attrs').get('user.rgw.source_zone').encode("latin-1") + b'\x00\x00\x00\x00') + assert((data.get(b'user.rgw.x-amz-date')) == attrs.get('attrs').get('user.rgw.x-amz-date').encode("latin-1") + b'\x00') + + tmp1 = '\x08\x06L\x01\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x06\x06\x84\x00\x00\x00\n\nj\x00\x00\x00\x03\x00\x00\x00bkt+\x00\x00\x00' + tmp2 = '+\x00\x00\x00' + tmp3 = '\x00\x00\x00\x00\x00\x00\x00\x00\x00\b\x00\x00\x00test.txt\x00\x00\x00\x00\x04\x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00!\x00\x00\x00' + tmp4 = '\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x02\x01 \x00\x00\x00\x00\x00\x00\x00\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ + '\x00\x00@\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x11\x00\x00\x00default-placement\x11\x00\x00\x00default-placement\x00\x00\x00\x00\x02\x02\x18' \ + '\x00\x00\x00\x04\x00\x00\x00none\x01\x01\t\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' + assert(data.get(b'user.rgw.manifest') == tmp1.encode("latin-1") + attrs.get('manifest').get('tail_placement').get('bucket').get('bucket_id').encode("utf-8") + + tmp2.encode("latin-1") + attrs.get('manifest').get('tail_placement').get('bucket').get('bucket_id').encode("utf-8") + + tmp3.encode("latin-1") + attrs.get('manifest').get('prefix').encode("utf-8") + + tmp4.encode("latin-1")) + + tmp5 = '\x02\x02\x81\x00\x00\x00\x03\x02\x11\x00\x00\x00\x06\x00\x00\x00s3main\x03\x00\x00\x00Foo\x04\x03d\x00\x00\x00\x01\x01\x00\x00\x00\x06\x00\x00' \ + '\x00s3main\x0f\x00\x00\x00\x01\x00\x00\x00\x06\x00\x00\x00s3main\x05\x035\x00\x00\x00\x02\x02\x04\x00\x00\x00\x00\x00\x00\x00\x06\x00\x00\x00s3main' \ + '\x00\x00\x00\x00\x00\x00\x00\x00\x02\x02\x04\x00\x00\x00\x0f\x00\x00\x00\x03\x00\x00\x00Foo\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00' \ + '\x00\x00\x00' + assert((data.get(b'user.rgw.acl')) == tmp5.encode("latin-1")) + + # getObject call + response_get = obj.get() + + assert(response_get.get('ResponseMetadata').get('HTTPStatusCode') == 200) + + # Copy to new object with 'COPY' directive; metadata value should not change + obj.metadata.update({'test':'value'}) + m = obj.metadata + m['test'] = 'value_replace' + + # copyObject call + client.copy_object(Bucket='bkt', Key='test_copy.txt', CopySource='bkt/test.txt', Metadata = m, MetadataDirective='COPY') + + assert(r.hexists('rgw-object:test_copy.txt:cache', b'user.rgw.x-amz-meta-test') == 0) + + # Update object with 'REPLACE' directive; metadata value should change + client.copy_object(Bucket='bkt', Key='test.txt', CopySource='bkt/test.txt', Metadata = m, MetadataDirective='REPLACE') + + data = r.hget('rgw-object:test.txt:cache', b'user.rgw.x-amz-meta-test') + + assert(data == b'value_replace\x00') + + # Ensure cache entry exists in cache before deletion + assert(r.exists('rgw-object:test.txt:cache') == True) + + # delObject call + response_del = obj.delete() + + assert(response_del.get('ResponseMetadata').get('HTTPStatusCode') == 204) + assert(r.exists('rgw-object:test.txt:cache') == False) + + r.flushall() + +def main(): + """ + execute the d4n test + """ + + # Setup for test + log.info("D4NFilterTest setup.") + + out = exec_cmd('pwd') + pwd = get_cmd_output(out) + log.debug("pwd is: %s", pwd) + + endpoint, proto = get_radosgw_endpoint() + + client = boto3.client(service_name='s3', + aws_access_key_id=ACCESS_KEY, + aws_secret_access_key=SECRET_KEY, + endpoint_url=endpoint, + use_ssl=False, + verify=False) + + s3 = boto3.resource('s3', + aws_access_key_id=ACCESS_KEY, + aws_secret_access_key=SECRET_KEY, + endpoint_url=endpoint, + use_ssl=False, + verify=False) + + bucket = s3.Bucket('bkt') + bucket.create() + obj = s3.Object(bucket_name='bkt', key='test.txt') + + # Check for Redis instance + try: + connection = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) + connection.ping() + except: + log.debug("ERROR: Redis instance not running.") + raise + + # Create s3cmd config + s3cmd_config_path = pwd + '/s3cfg' + create_s3cmd_config(s3cmd_config_path, proto) + + r = redis.Redis(host='localhost', port=6379, db=0, decode_responses=True) + + test_directory_methods(r, client, obj) + + # Responses should not be decoded + r = redis.Redis(host='localhost', port=6379, db=0) + + test_cache_methods(r, client, obj) + + log.info("D4NFilterTest successfully completed.") + +main() +log.info("Completed D4N tests") diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt index 42eb093f330f..1297df32405a 100644 --- a/src/common/CMakeLists.txt +++ b/src/common/CMakeLists.txt @@ -186,7 +186,6 @@ add_library(common-common-objs OBJECT # retrieved OpenSSL location. This is especially important when cross # compiling (e.g. targeting Windows). target_include_directories(common-common-objs PRIVATE ${OPENSSL_INCLUDE_DIR}) - # for options.cc target_compile_definitions(common-common-objs PRIVATE "CMAKE_INSTALL_LIBDIR=\"${CMAKE_INSTALL_LIBDIR}\""