+++ /dev/null
-#! /usr/bin/python
-
-from subprocess import call
-from subprocess import check_output
-import os
-import time
-import sys
-import re
-import string
-import logging
-
-logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.WARNING)
-
-
-def wait_for_health():
- print "Wait for health_ok...",
- while call("./ceph health 2> /dev/null | grep -v HEALTH_OK > /dev/null", shell=True) == 0:
- time.sleep(5)
- print "DONE"
-
-
-def get_pool_id(name, nullfd):
- cmd = "./ceph osd pool stats {pool}".format(pool=name).split()
- # pool {pool} id # .... grab the 4 field
- return check_output(cmd, stderr=nullfd).split()[3]
-
-
-# return a sorted list of unique PGs given a directory
-def get_pgs(DIR, ID):
- OSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
- PGS = []
- endhead = re.compile("{id}.*_head$".format(id=ID))
- for d in OSDS:
- DIRL2 = os.path.join(DIR, d)
- SUBDIR = os.path.join(DIRL2, "current")
- PGS += [f for f in os.listdir(SUBDIR) if os.path.isdir(os.path.join(SUBDIR, f)) and endhead.match(f)]
- PGS = [re.sub("_head", "", p) for p in PGS]
- return sorted(set(PGS))
-
-
-# return a sorted list of PGS a subset of ALLPGS that contain objects with prefix specified
-def get_objs(ALLPGS, prefix, DIR, ID):
- OSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
- PGS = []
- for d in OSDS:
- DIRL2 = os.path.join(DIR, d)
- SUBDIR = os.path.join(DIRL2, "current")
- for p in ALLPGS:
- PGDIR = p + "_head"
- if not os.path.isdir(os.path.join(SUBDIR, PGDIR)):
- continue
- FINALDIR = os.path.join(SUBDIR, PGDIR)
- # See if there are any objects there
- if [f for f in os.listdir(FINALDIR) if os.path.isfile(os.path.join(FINALDIR, f)) and string.find(f, prefix) == 0]:
- PGS += [p]
- return sorted(set(PGS))
-
-
-# return a sorted list of OSDS which have data from a given PG
-def get_osds(PG, DIR):
- ALLOSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
- OSDS = []
- for d in ALLOSDS:
- DIRL2 = os.path.join(DIR, d)
- SUBDIR = os.path.join(DIRL2, "current")
- PGDIR = PG + "_head"
- if not os.path.isdir(os.path.join(SUBDIR, PGDIR)):
- continue
- OSDS += [d]
- return sorted(OSDS)
-
-
-def get_lines(filename):
- tmpfd = open(filename, "r")
- line = True
- lines = []
- while line:
- line = tmpfd.readline().rstrip('\n')
- if line:
- lines += [line]
- tmpfd.close()
- os.unlink(filename)
- return lines
-
-
-def cat_file(level, filename):
- if level < logging.getLogger().getEffectiveLevel():
- return
- print "File: " + filename
- with open(filename, "r") as f:
- while True:
- line = f.readline().rstrip('\n')
- if not line:
- break
- print line
- print "<EOF>"
-
-
-def vstart(new):
- print "vstarting....",
- OPT = new and "-n" or ""
- call("OSD=4 ./vstart.sh -l {opt} -d > /dev/null 2>&1".format(opt=OPT), shell=True)
- print "DONE"
-
-
-def test_failure(cmd, errmsg):
- ttyfd = open("/dev/tty", "rw")
- TMPFILE = r"/tmp/tmp.{pid}".format(pid=os.getpid())
- tmpfd = open(TMPFILE, "w")
-
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdin=ttyfd, stdout=ttyfd, stderr=tmpfd)
- ttyfd.close()
- tmpfd.close()
- if ret == 0:
- logging.error("Should have failed, but got exit 0")
- return 1
- lines = get_lines(TMPFILE)
- line = lines[0]
- if line == errmsg:
- logging.info("Correctly failed with message \"" + line + "\"")
- return 0
- else:
- logging.error("Bad message to stderr \"" + line + "\"")
- return 1
-
-
-def main():
- sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
- nullfd = open(os.devnull, "w")
-
- OSDDIR = "dev"
- REP_POOL = "rep_pool"
- REP_NAME = "REPobject"
- EC_POOL = "ec_pool"
- EC_NAME = "ECobject"
- NUM_OBJECTS = 40
- ERRORS = 0
- pid = os.getpid()
- TESTDIR = "/tmp/test.{pid}".format(pid=pid)
- DATADIR = "/tmp/data.{pid}".format(pid=pid)
- CFSD_PREFIX = "./ceph_filestore_dump --filestore-path dev/{osd} --journal-path dev/{osd}.journal "
- DATALINECOUNT = 10000
- PROFNAME = "testecprofile"
-
- vstart(new=True)
- wait_for_health()
-
- cmd = "./ceph osd pool create {pool} 12 12 replicated".format(pool=REP_POOL)
- logging.debug(cmd)
- call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
- REPID = get_pool_id(REP_POOL, nullfd)
-
- print "Created Replicated pool #{repid}".format(repid=REPID)
-
- cmd = "./ceph osd erasure-code-profile set {prof} ruleset-failure-domain=osd".format(prof=PROFNAME)
- logging.debug(cmd)
- call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
- cmd = "./ceph osd erasure-code-profile get {prof}".format(prof=PROFNAME)
- logging.debug(cmd)
- call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
- cmd = "./ceph osd pool create {pool} 12 12 erasure {prof}".format(pool=EC_POOL, prof=PROFNAME)
- logging.debug(cmd)
- call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
- ECID = get_pool_id(EC_POOL, nullfd)
-
- print "Created Erasure coded pool #{ecid}".format(ecid=ECID)
-
- print "Creating {objs} objects in replicated pool".format(objs=NUM_OBJECTS)
- cmd = "mkdir -p {datadir}".format(datadir=DATADIR)
- logging.debug(cmd)
- call(cmd, shell=True)
-
- db = {}
-
- objects = range(1, NUM_OBJECTS + 1)
- for i in objects:
- NAME = REP_NAME + "{num}".format(num=i)
- DDNAME = os.path.join(DATADIR, NAME)
-
- cmd = "rm -f " + DDNAME
- logging.debug(cmd)
- call(cmd, shell=True)
-
- dataline = range(DATALINECOUNT)
- fd = open(DDNAME, "w")
- data = "This is the replicated data for " + NAME + "\n"
- for _ in dataline:
- fd.write(data)
- fd.close()
-
- cmd = "./rados -p {pool} put {name} {ddname}".format(pool=REP_POOL, name=NAME, ddname=DDNAME)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stderr=nullfd)
- if ret != 0:
- logging.critical("Replicated pool creation failed with {ret}".format(ret=ret))
- sys.exit(1)
-
- db[NAME] = {}
-
- keys = range(i)
- db[NAME]["xattr"] = {}
- for k in keys:
- if k == 0:
- continue
- mykey = "key{i}-{k}".format(i=i, k=k)
- myval = "val{i}-{k}".format(i=i, k=k)
- cmd = "./rados -p {pool} setxattr {name} {key} {val}".format(pool=REP_POOL, name=NAME, key=mykey, val=myval)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.error("setxattr failed with {ret}".format(ret=ret))
- ERRORS += 1
- db[NAME]["xattr"][mykey] = myval
-
- # Create omap header in all objects but REPobject1
- if i != 1:
- myhdr = "hdr{i}".format(i=i)
- cmd = "./rados -p {pool} setomapheader {name} {hdr}".format(pool=REP_POOL, name=NAME, hdr=myhdr)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.critical("setomapheader failed with {ret}".format(ret=ret))
- ERRORS += 1
- db[NAME]["omapheader"] = myhdr
-
- db[NAME]["omap"] = {}
- for k in keys:
- if k == 0:
- continue
- mykey = "okey{i}-{k}".format(i=i, k=k)
- myval = "oval{i}-{k}".format(i=i, k=k)
- cmd = "./rados -p {pool} setomapval {name} {key} {val}".format(pool=REP_POOL, name=NAME, key=mykey, val=myval)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.critical("setomapval failed with {ret}".format(ret=ret))
- db[NAME]["omap"][mykey] = myval
-
- print "Creating {objs} objects in erasure coded pool".format(objs=NUM_OBJECTS)
-
- for i in objects:
- NAME = EC_NAME + "{num}".format(num=i)
- DDNAME = os.path.join(DATADIR, NAME)
-
- cmd = "rm -f " + DDNAME
- logging.debug(cmd)
- call(cmd, shell=True)
-
- fd = open(DDNAME, "w")
- data = "This is the erasure coded data for " + NAME + "\n"
- for j in dataline:
- fd.write(data)
- fd.close()
-
- cmd = "./rados -p {pool} put {name} {ddname}".format(pool=EC_POOL, name=NAME, ddname=DDNAME)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stderr=nullfd)
- if ret != 0:
- logging.critical("Erasure coded pool creation failed with {ret}".format(ret=ret))
- sys.exit(1)
-
- db[NAME] = {}
-
- db[NAME]["xattr"] = {}
- keys = range(i)
- for k in keys:
- if k == 0:
- continue
- mykey = "key{i}-{k}".format(i=i, k=k)
- myval = "val{i}-{k}".format(i=i, k=k)
- cmd = "./rados -p {pool} setxattr {name} {key} {val}".format(pool=EC_POOL, name=NAME, key=mykey, val=myval)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.error("setxattr failed with {ret}".format(ret=ret))
- ERRORS += 1
- db[NAME]["xattr"][mykey] = myval
-
- # Omap isn't supported in EC pools
- db[NAME]["omap"] = {}
-
- logging.debug(db)
-
-
- call("./stop.sh", stderr=nullfd)
-
- if ERRORS:
- logging.critical("Unable to set up test")
- sys.exit(1)
-
- ALLREPPGS = get_pgs(OSDDIR, REPID)
- logging.debug(ALLREPPGS)
- ALLECPGS = get_pgs(OSDDIR, ECID)
- logging.debug(ALLECPGS)
-
- OBJREPPGS = get_objs(ALLREPPGS, REP_NAME, OSDDIR, REPID)
- logging.debug(OBJREPPGS)
- OBJECPGS = get_objs(ALLECPGS, EC_NAME, OSDDIR, ECID)
- logging.debug(OBJECPGS)
-
- ONEPG = ALLREPPGS[0]
- logging.debug(ONEPG)
- osds = get_osds(ONEPG, OSDDIR)
- ONEOSD = osds[0]
- logging.debug(ONEOSD)
-
- print "Test invalid parameters"
- # On export can't use stdout to a terminal
- cmd = (CFSD_PREFIX + "--type export --pgid {pg}").format(osd=ONEOSD, pg=ONEPG)
- ERRORS += test_failure(cmd, "stdout is a tty and no --file option specified")
-
- OTHERFILE = "/tmp/foo.{pid}".format(pid=pid)
- foofd = open(OTHERFILE, "w")
- foofd.close()
-
- # On import can't specify a PG
- cmd = (CFSD_PREFIX + "--type import --pgid {pg} --file {FOO}").format(osd=ONEOSD, pg=ONEPG, FOO=OTHERFILE)
- ERRORS += test_failure(cmd, "--pgid option invalid with import")
-
- os.unlink(OTHERFILE)
- cmd = (CFSD_PREFIX + "--type import --file {FOO}").format(osd=ONEOSD, FOO=OTHERFILE)
- ERRORS += test_failure(cmd, "open: No such file or directory")
-
- # On import can't use stdin from a terminal
- cmd = (CFSD_PREFIX + "--type import --pgid {pg}").format(osd=ONEOSD, pg=ONEPG)
- ERRORS += test_failure(cmd, "stdin is a tty and no --file option specified")
-
- # Test --type list and generate json for all objects
- print "Test --type list by generating json for all objects"
- TMPFILE = r"/tmp/tmp.{pid}".format(pid=pid)
- ALLPGS = OBJREPPGS + OBJECPGS
- for pg in ALLPGS:
- OSDS = get_osds(pg, OSDDIR)
- for osd in OSDS:
- cmd = (CFSD_PREFIX + "--type list --pgid {pg}").format(osd=osd, pg=pg)
- tmpfd = open(TMPFILE, "a")
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=tmpfd)
- if ret != 0:
- logging.error("Bad exit status {ret} from --type list request".format(ret=ret))
- ERRORS += 1
-
- tmpfd.close()
- lines = get_lines(TMPFILE)
- JSONOBJ = sorted(set(lines))
-
- # Test get-bytes
- print "Test get-bytes and set-bytes"
- for basename in db.keys():
- file = os.path.join(DATADIR, basename)
- JSON = [l for l in JSONOBJ if l.find("\"" + basename + "\"") != -1]
- JSON = JSON[0]
- GETNAME = "/tmp/getbytes.{pid}".format(pid=pid)
- TESTNAME = "/tmp/testbytes.{pid}".format(pid=pid)
- SETNAME = "/tmp/setbytes.{pid}".format(pid=pid)
- for pg in OBJREPPGS:
- OSDS = get_osds(pg, OSDDIR)
- for osd in OSDS:
- DIR = os.path.join(OSDDIR, os.path.join(osd, os.path.join("current", "{pg}_head".format(pg=pg))))
- fname = [f for f in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, f)) and string.find(f, basename + "_") == 0]
- if not fname:
- continue
- fname = fname[0]
- try:
- os.unlink(GETNAME)
- except:
- pass
- cmd = (CFSD_PREFIX + " --pgid {pg} '{json}' get-bytes {fname}").format(osd=osd, pg=pg, json=JSON, fname=GETNAME)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.error("Bad exit status {ret}".format(ret=ret))
- ERRORS += 1
- continue
- cmd = "diff -q {file} {getfile}".format(file=file, getfile=GETNAME)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.error("Data from get-bytes differ")
- logging.debug("Got:")
- cat_file(logging.DEBUG, GETNAME)
- logging.debug("Expected:")
- cat_file(logging.DEBUG, file)
- ERRORS += 1
- fd = open(SETNAME, "w")
- data = "put-bytes going into {file}\n".format(file=file)
- fd.write(data)
- fd.close()
- cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' set-bytes {sname}").format(osd=osd, pg=pg, json=JSON, sname=SETNAME)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.error("Bad exit status {ret} from set-bytes".format(ret=ret))
- ERRORS += 1
- fd = open(TESTNAME, "w")
- cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' get-bytes -").format(osd=osd, pg=pg, json=JSON)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=fd)
- fd.close()
- if ret != 0:
- logging.error("Bad exit status {ret} from get-bytes".format(ret=ret))
- ERRORS += 1
- cmd = "diff -q {setfile} {testfile}".format(setfile=SETNAME, testfile=TESTNAME)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.error("Data after set-bytes differ")
- logging.debug("Got:")
- cat_file(logging.DEBUG, TESTNAME)
- logging.debug("Expected:")
- cat_file(logging.DEBUG, SETNAME)
- ERRORS += 1
- fd = open(file, "r")
- cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' set-bytes").format(osd=osd, pg=pg, json=JSON)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdin=fd)
- if ret != 0:
- logging.error("Bad exit status {ret} from set-bytes to restore object".format(ret=ret))
- ERRORS += 1
-
- try:
- os.unlink(GETNAME)
- except:
- pass
- try:
- os.unlink(TESTNAME)
- except:
- pass
- try:
- os.unlink(SETNAME)
- except:
- pass
-
- print "Test list-attrs get-attr"
- ATTRFILE = r"/tmp/attrs.{pid}".format(pid=pid)
- VALFILE = r"/tmp/val.{pid}".format(pid=pid)
- for basename in db.keys():
- file = os.path.join(DATADIR, basename)
- JSON = [l for l in JSONOBJ if l.find("\"" + basename + "\"") != -1]
- JSON = JSON[0]
- for pg in OBJREPPGS:
- OSDS = get_osds(pg, OSDDIR)
- for osd in OSDS:
- DIR = os.path.join(OSDDIR, os.path.join(osd, os.path.join("current", "{pg}_head".format(pg=pg))))
- fname = [f for f in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, f)) and string.find(f, basename + "_") == 0]
- if not fname:
- continue
- fname = fname[0]
- afd = open(ATTRFILE, "w")
- cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' list-attrs").format(osd=osd, pg=pg, json=JSON)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=afd)
- afd.close()
- if ret != 0:
- logging.error("list-attrs failed with {ret}".format(ret=ret))
- ERRORS += 1
- continue
- keys = get_lines(ATTRFILE)
- values = dict(db[basename]["xattr"])
- for key in keys:
- if key == "_" or key == "snapset":
- continue
- key = key.strip("_")
- if key not in values:
- logging.error("The key {key} should be present".format(key=key))
- ERRORS += 1
- continue
- exp = values.pop(key)
- vfd = open(VALFILE, "w")
- cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' get-attr {key}").format(osd=osd, pg=pg, json=JSON, key="_" + key)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=vfd)
- vfd.close()
- if ret != 0:
- logging.error("get-attr failed with {ret}".format(ret=ret))
- ERRORS += 1
- continue
- lines = get_lines(VALFILE)
- val = lines[0]
- if exp != val:
- logging.error("For key {key} got value {got} instead of {expected}".format(key=key, got=val, expected=exp))
- ERRORS += 1
- if len(values) != 0:
- logging.error("Not all keys found, remaining keys:")
- print values
-
- print "Test pg info"
- for pg in ALLREPPGS + ALLECPGS:
- for osd in get_osds(pg, OSDDIR):
- cmd = (CFSD_PREFIX + "--type info --pgid {pg} | grep '\"pgid\": \"{pg}\"'").format(osd=osd, pg=pg)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=nullfd)
- if ret != 0:
- logging.error("Getting info failed for pg {pg} from {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
- ERRORS += 1
-
- print "Test pg logging"
- for pg in ALLREPPGS + ALLECPGS:
- for osd in get_osds(pg, OSDDIR):
- tmpfd = open(TMPFILE, "w")
- cmd = (CFSD_PREFIX + "--type log --pgid {pg}").format(osd=osd, pg=pg)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=tmpfd)
- if ret != 0:
- logging.error("Getting log failed for pg {pg} from {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
- ERRORS += 1
- HASOBJ = pg in OBJREPPGS + OBJECPGS
- MODOBJ = False
- for line in get_lines(TMPFILE):
- if line.find("modify") != -1:
- MODOBJ = True
- break
- if HASOBJ != MODOBJ:
- logging.error("Bad log for pg {pg} from {osd}".format(pg=pg, osd=osd))
- MSG = (HASOBJ and [""] or ["NOT "])[0]
- print "Log should {msg}have a modify entry".format(msg=MSG)
- ERRORS += 1
-
- try:
- os.unlink(TMPFILE)
- except:
- pass
-
- print "Test pg export"
- EXP_ERRORS = 0
- os.mkdir(TESTDIR)
- for osd in [f for f in os.listdir(OSDDIR) if os.path.isdir(os.path.join(OSDDIR, f)) and string.find(f, "osd") == 0]:
- os.mkdir(os.path.join(TESTDIR, osd))
- for pg in ALLREPPGS + ALLECPGS:
- for osd in get_osds(pg, OSDDIR):
- mydir = os.path.join(TESTDIR, osd)
- fname = os.path.join(mydir, pg)
- cmd = (CFSD_PREFIX + "--type export --pgid {pg} --file {file}").format(osd=osd, pg=pg, file=fname)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
- if ret != 0:
- logging.error("Exporting failed for pg {pg} on {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
- EXP_ERRORS += 1
-
- ERRORS += EXP_ERRORS
-
- print "Test pg removal"
- RM_ERRORS = 0
- for pg in ALLREPPGS + ALLECPGS:
- for osd in get_osds(pg, OSDDIR):
- cmd = (CFSD_PREFIX + "--type remove --pgid {pg}").format(pg=pg, osd=osd)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=nullfd)
- if ret != 0:
- logging.error("Removing failed for pg {pg} on {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
- RM_ERRORS += 1
-
- ERRORS += RM_ERRORS
-
- IMP_ERRORS = 0
- if EXP_ERRORS == 0 and RM_ERRORS == 0:
- print "Test pg import"
- for osd in [f for f in os.listdir(OSDDIR) if os.path.isdir(os.path.join(OSDDIR, f)) and string.find(f, "osd") == 0]:
- dir = os.path.join(TESTDIR, osd)
- for pg in [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]:
- file = os.path.join(dir, pg)
- cmd = (CFSD_PREFIX + "--type import --file {file}").format(osd=osd, file=file)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=nullfd)
- if ret != 0:
- logging.error("Import failed from {file} with {ret}".format(file=file, ret=ret))
- IMP_ERRORS += 1
- else:
- logging.warning("SKIPPING IMPORT TESTS DUE TO PREVIOUS FAILURES")
-
- ERRORS += IMP_ERRORS
- logging.debug(cmd)
- call("/bin/rm -rf {dir}".format(dir=TESTDIR), shell=True)
-
- if EXP_ERRORS == 0 and RM_ERRORS == 0 and IMP_ERRORS == 0:
- print "Verify replicated import data"
- for file in [f for f in os.listdir(DATADIR) if f.find(REP_NAME) == 0]:
- path = os.path.join(DATADIR, file)
- tmpfd = open(TMPFILE, "w")
- cmd = "find {dir} -name '{file}_*'".format(dir=OSDDIR, file=file)
- logging.debug(cmd)
- ret = call(cmd, shell=True, stdout=tmpfd)
- if ret:
- logging.critical("INTERNAL ERROR")
- sys.exit(1)
- tmpfd.close()
- obj_locs = get_lines(TMPFILE)
- if len(obj_locs) == 0:
- logging.error("Can't find imported object {name}".format(name=file))
- ERRORS += 1
- for obj_loc in obj_locs:
- cmd = "diff -q {src} {obj_loc}".format(src=path, obj_loc=obj_loc)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.error("{file} data not imported properly into {obj}".format(file=file, obj=obj_loc))
- ERRORS += 1
-
- vstart(new=False)
- wait_for_health()
-
- print "Verify erasure coded import data"
- for file in [f for f in os.listdir(DATADIR) if f.find(EC_NAME) == 0]:
- path = os.path.join(DATADIR, file)
- try:
- os.unlink(TMPFILE)
- except:
- pass
- cmd = "./rados -p {pool} get {file} {out}".format(pool=EC_POOL, file=file, out=TMPFILE)
- logging.debug(cmd)
- call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
- cmd = "diff -q {src} {result}".format(src=path, result=TMPFILE)
- logging.debug(cmd)
- ret = call(cmd, shell=True)
- if ret != 0:
- logging.error("{file} data not imported properly".format(file=file))
- ERRORS += 1
- try:
- os.unlink(TMPFILE)
- except:
- pass
-
- call("./stop.sh", stderr=nullfd)
- else:
- logging.warning("SKIPPING CHECKING IMPORT DATA DUE TO PREVIOUS FAILURES")
-
- call("/bin/rm -rf {dir}".format(dir=DATADIR), shell=True)
-
- if ERRORS == 0:
- print "TEST PASSED"
- sys.exit(0)
- else:
- print "TEST FAILED WITH {errcount} ERRORS".format(errcount=ERRORS)
- sys.exit(1)
-
-if __name__ == "__main__":
- main()
--- /dev/null
+#! /usr/bin/python
+
+from subprocess import call
+from subprocess import check_output
+import os
+import time
+import sys
+import re
+import string
+import logging
+
+logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.WARNING)
+
+
+def wait_for_health():
+ print "Wait for health_ok...",
+ while call("./ceph health 2> /dev/null | grep -v HEALTH_OK > /dev/null", shell=True) == 0:
+ time.sleep(5)
+ print "DONE"
+
+
+def get_pool_id(name, nullfd):
+ cmd = "./ceph osd pool stats {pool}".format(pool=name).split()
+ # pool {pool} id # .... grab the 4 field
+ return check_output(cmd, stderr=nullfd).split()[3]
+
+
+# return a sorted list of unique PGs given a directory
+def get_pgs(DIR, ID):
+ OSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
+ PGS = []
+ endhead = re.compile("{id}.*_head$".format(id=ID))
+ for d in OSDS:
+ DIRL2 = os.path.join(DIR, d)
+ SUBDIR = os.path.join(DIRL2, "current")
+ PGS += [f for f in os.listdir(SUBDIR) if os.path.isdir(os.path.join(SUBDIR, f)) and endhead.match(f)]
+ PGS = [re.sub("_head", "", p) for p in PGS]
+ return sorted(set(PGS))
+
+
+# return a sorted list of PGS a subset of ALLPGS that contain objects with prefix specified
+def get_objs(ALLPGS, prefix, DIR, ID):
+ OSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
+ PGS = []
+ for d in OSDS:
+ DIRL2 = os.path.join(DIR, d)
+ SUBDIR = os.path.join(DIRL2, "current")
+ for p in ALLPGS:
+ PGDIR = p + "_head"
+ if not os.path.isdir(os.path.join(SUBDIR, PGDIR)):
+ continue
+ FINALDIR = os.path.join(SUBDIR, PGDIR)
+ # See if there are any objects there
+ if [f for f in os.listdir(FINALDIR) if os.path.isfile(os.path.join(FINALDIR, f)) and string.find(f, prefix) == 0]:
+ PGS += [p]
+ return sorted(set(PGS))
+
+
+# return a sorted list of OSDS which have data from a given PG
+def get_osds(PG, DIR):
+ ALLOSDS = [f for f in os.listdir(DIR) if os.path.isdir(os.path.join(DIR, f)) and string.find(f, "osd") == 0]
+ OSDS = []
+ for d in ALLOSDS:
+ DIRL2 = os.path.join(DIR, d)
+ SUBDIR = os.path.join(DIRL2, "current")
+ PGDIR = PG + "_head"
+ if not os.path.isdir(os.path.join(SUBDIR, PGDIR)):
+ continue
+ OSDS += [d]
+ return sorted(OSDS)
+
+
+def get_lines(filename):
+ tmpfd = open(filename, "r")
+ line = True
+ lines = []
+ while line:
+ line = tmpfd.readline().rstrip('\n')
+ if line:
+ lines += [line]
+ tmpfd.close()
+ os.unlink(filename)
+ return lines
+
+
+def cat_file(level, filename):
+ if level < logging.getLogger().getEffectiveLevel():
+ return
+ print "File: " + filename
+ with open(filename, "r") as f:
+ while True:
+ line = f.readline().rstrip('\n')
+ if not line:
+ break
+ print line
+ print "<EOF>"
+
+
+def vstart(new):
+ print "vstarting....",
+ OPT = new and "-n" or ""
+ call("OSD=4 ./vstart.sh -l {opt} -d > /dev/null 2>&1".format(opt=OPT), shell=True)
+ print "DONE"
+
+
+def test_failure(cmd, errmsg):
+ ttyfd = open("/dev/tty", "rw")
+ TMPFILE = r"/tmp/tmp.{pid}".format(pid=os.getpid())
+ tmpfd = open(TMPFILE, "w")
+
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdin=ttyfd, stdout=ttyfd, stderr=tmpfd)
+ ttyfd.close()
+ tmpfd.close()
+ if ret == 0:
+ logging.error("Should have failed, but got exit 0")
+ return 1
+ lines = get_lines(TMPFILE)
+ line = lines[0]
+ if line == errmsg:
+ logging.info("Correctly failed with message \"" + line + "\"")
+ return 0
+ else:
+ logging.error("Bad message to stderr \"" + line + "\"")
+ return 1
+
+
+def main():
+ sys.stdout = os.fdopen(sys.stdout.fileno(), 'w', 0)
+ nullfd = open(os.devnull, "w")
+
+ OSDDIR = "dev"
+ REP_POOL = "rep_pool"
+ REP_NAME = "REPobject"
+ EC_POOL = "ec_pool"
+ EC_NAME = "ECobject"
+ NUM_OBJECTS = 40
+ ERRORS = 0
+ pid = os.getpid()
+ TESTDIR = "/tmp/test.{pid}".format(pid=pid)
+ DATADIR = "/tmp/data.{pid}".format(pid=pid)
+ CFSD_PREFIX = "./ceph_filestore_dump --filestore-path dev/{osd} --journal-path dev/{osd}.journal "
+ DATALINECOUNT = 10000
+ PROFNAME = "testecprofile"
+
+ vstart(new=True)
+ wait_for_health()
+
+ cmd = "./ceph osd pool create {pool} 12 12 replicated".format(pool=REP_POOL)
+ logging.debug(cmd)
+ call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+ REPID = get_pool_id(REP_POOL, nullfd)
+
+ print "Created Replicated pool #{repid}".format(repid=REPID)
+
+ cmd = "./ceph osd erasure-code-profile set {prof} ruleset-failure-domain=osd".format(prof=PROFNAME)
+ logging.debug(cmd)
+ call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+ cmd = "./ceph osd erasure-code-profile get {prof}".format(prof=PROFNAME)
+ logging.debug(cmd)
+ call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+ cmd = "./ceph osd pool create {pool} 12 12 erasure {prof}".format(pool=EC_POOL, prof=PROFNAME)
+ logging.debug(cmd)
+ call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+ ECID = get_pool_id(EC_POOL, nullfd)
+
+ print "Created Erasure coded pool #{ecid}".format(ecid=ECID)
+
+ print "Creating {objs} objects in replicated pool".format(objs=NUM_OBJECTS)
+ cmd = "mkdir -p {datadir}".format(datadir=DATADIR)
+ logging.debug(cmd)
+ call(cmd, shell=True)
+
+ db = {}
+
+ objects = range(1, NUM_OBJECTS + 1)
+ for i in objects:
+ NAME = REP_NAME + "{num}".format(num=i)
+ DDNAME = os.path.join(DATADIR, NAME)
+
+ cmd = "rm -f " + DDNAME
+ logging.debug(cmd)
+ call(cmd, shell=True)
+
+ dataline = range(DATALINECOUNT)
+ fd = open(DDNAME, "w")
+ data = "This is the replicated data for " + NAME + "\n"
+ for _ in dataline:
+ fd.write(data)
+ fd.close()
+
+ cmd = "./rados -p {pool} put {name} {ddname}".format(pool=REP_POOL, name=NAME, ddname=DDNAME)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stderr=nullfd)
+ if ret != 0:
+ logging.critical("Replicated pool creation failed with {ret}".format(ret=ret))
+ sys.exit(1)
+
+ db[NAME] = {}
+
+ keys = range(i)
+ db[NAME]["xattr"] = {}
+ for k in keys:
+ if k == 0:
+ continue
+ mykey = "key{i}-{k}".format(i=i, k=k)
+ myval = "val{i}-{k}".format(i=i, k=k)
+ cmd = "./rados -p {pool} setxattr {name} {key} {val}".format(pool=REP_POOL, name=NAME, key=mykey, val=myval)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.error("setxattr failed with {ret}".format(ret=ret))
+ ERRORS += 1
+ db[NAME]["xattr"][mykey] = myval
+
+ # Create omap header in all objects but REPobject1
+ if i != 1:
+ myhdr = "hdr{i}".format(i=i)
+ cmd = "./rados -p {pool} setomapheader {name} {hdr}".format(pool=REP_POOL, name=NAME, hdr=myhdr)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.critical("setomapheader failed with {ret}".format(ret=ret))
+ ERRORS += 1
+ db[NAME]["omapheader"] = myhdr
+
+ db[NAME]["omap"] = {}
+ for k in keys:
+ if k == 0:
+ continue
+ mykey = "okey{i}-{k}".format(i=i, k=k)
+ myval = "oval{i}-{k}".format(i=i, k=k)
+ cmd = "./rados -p {pool} setomapval {name} {key} {val}".format(pool=REP_POOL, name=NAME, key=mykey, val=myval)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.critical("setomapval failed with {ret}".format(ret=ret))
+ db[NAME]["omap"][mykey] = myval
+
+ print "Creating {objs} objects in erasure coded pool".format(objs=NUM_OBJECTS)
+
+ for i in objects:
+ NAME = EC_NAME + "{num}".format(num=i)
+ DDNAME = os.path.join(DATADIR, NAME)
+
+ cmd = "rm -f " + DDNAME
+ logging.debug(cmd)
+ call(cmd, shell=True)
+
+ fd = open(DDNAME, "w")
+ data = "This is the erasure coded data for " + NAME + "\n"
+ for j in dataline:
+ fd.write(data)
+ fd.close()
+
+ cmd = "./rados -p {pool} put {name} {ddname}".format(pool=EC_POOL, name=NAME, ddname=DDNAME)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stderr=nullfd)
+ if ret != 0:
+ logging.critical("Erasure coded pool creation failed with {ret}".format(ret=ret))
+ sys.exit(1)
+
+ db[NAME] = {}
+
+ db[NAME]["xattr"] = {}
+ keys = range(i)
+ for k in keys:
+ if k == 0:
+ continue
+ mykey = "key{i}-{k}".format(i=i, k=k)
+ myval = "val{i}-{k}".format(i=i, k=k)
+ cmd = "./rados -p {pool} setxattr {name} {key} {val}".format(pool=EC_POOL, name=NAME, key=mykey, val=myval)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.error("setxattr failed with {ret}".format(ret=ret))
+ ERRORS += 1
+ db[NAME]["xattr"][mykey] = myval
+
+ # Omap isn't supported in EC pools
+ db[NAME]["omap"] = {}
+
+ logging.debug(db)
+
+
+ call("./stop.sh", stderr=nullfd)
+
+ if ERRORS:
+ logging.critical("Unable to set up test")
+ sys.exit(1)
+
+ ALLREPPGS = get_pgs(OSDDIR, REPID)
+ logging.debug(ALLREPPGS)
+ ALLECPGS = get_pgs(OSDDIR, ECID)
+ logging.debug(ALLECPGS)
+
+ OBJREPPGS = get_objs(ALLREPPGS, REP_NAME, OSDDIR, REPID)
+ logging.debug(OBJREPPGS)
+ OBJECPGS = get_objs(ALLECPGS, EC_NAME, OSDDIR, ECID)
+ logging.debug(OBJECPGS)
+
+ ONEPG = ALLREPPGS[0]
+ logging.debug(ONEPG)
+ osds = get_osds(ONEPG, OSDDIR)
+ ONEOSD = osds[0]
+ logging.debug(ONEOSD)
+
+ print "Test invalid parameters"
+ # On export can't use stdout to a terminal
+ cmd = (CFSD_PREFIX + "--type export --pgid {pg}").format(osd=ONEOSD, pg=ONEPG)
+ ERRORS += test_failure(cmd, "stdout is a tty and no --file option specified")
+
+ OTHERFILE = "/tmp/foo.{pid}".format(pid=pid)
+ foofd = open(OTHERFILE, "w")
+ foofd.close()
+
+ # On import can't specify a PG
+ cmd = (CFSD_PREFIX + "--type import --pgid {pg} --file {FOO}").format(osd=ONEOSD, pg=ONEPG, FOO=OTHERFILE)
+ ERRORS += test_failure(cmd, "--pgid option invalid with import")
+
+ os.unlink(OTHERFILE)
+ cmd = (CFSD_PREFIX + "--type import --file {FOO}").format(osd=ONEOSD, FOO=OTHERFILE)
+ ERRORS += test_failure(cmd, "open: No such file or directory")
+
+ # On import can't use stdin from a terminal
+ cmd = (CFSD_PREFIX + "--type import --pgid {pg}").format(osd=ONEOSD, pg=ONEPG)
+ ERRORS += test_failure(cmd, "stdin is a tty and no --file option specified")
+
+ # Test --type list and generate json for all objects
+ print "Test --type list by generating json for all objects"
+ TMPFILE = r"/tmp/tmp.{pid}".format(pid=pid)
+ ALLPGS = OBJREPPGS + OBJECPGS
+ for pg in ALLPGS:
+ OSDS = get_osds(pg, OSDDIR)
+ for osd in OSDS:
+ cmd = (CFSD_PREFIX + "--type list --pgid {pg}").format(osd=osd, pg=pg)
+ tmpfd = open(TMPFILE, "a")
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=tmpfd)
+ if ret != 0:
+ logging.error("Bad exit status {ret} from --type list request".format(ret=ret))
+ ERRORS += 1
+
+ tmpfd.close()
+ lines = get_lines(TMPFILE)
+ JSONOBJ = sorted(set(lines))
+
+ # Test get-bytes
+ print "Test get-bytes and set-bytes"
+ for basename in db.keys():
+ file = os.path.join(DATADIR, basename)
+ JSON = [l for l in JSONOBJ if l.find("\"" + basename + "\"") != -1]
+ JSON = JSON[0]
+ GETNAME = "/tmp/getbytes.{pid}".format(pid=pid)
+ TESTNAME = "/tmp/testbytes.{pid}".format(pid=pid)
+ SETNAME = "/tmp/setbytes.{pid}".format(pid=pid)
+ for pg in OBJREPPGS:
+ OSDS = get_osds(pg, OSDDIR)
+ for osd in OSDS:
+ DIR = os.path.join(OSDDIR, os.path.join(osd, os.path.join("current", "{pg}_head".format(pg=pg))))
+ fname = [f for f in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, f)) and string.find(f, basename + "_") == 0]
+ if not fname:
+ continue
+ fname = fname[0]
+ try:
+ os.unlink(GETNAME)
+ except:
+ pass
+ cmd = (CFSD_PREFIX + " --pgid {pg} '{json}' get-bytes {fname}").format(osd=osd, pg=pg, json=JSON, fname=GETNAME)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.error("Bad exit status {ret}".format(ret=ret))
+ ERRORS += 1
+ continue
+ cmd = "diff -q {file} {getfile}".format(file=file, getfile=GETNAME)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.error("Data from get-bytes differ")
+ logging.debug("Got:")
+ cat_file(logging.DEBUG, GETNAME)
+ logging.debug("Expected:")
+ cat_file(logging.DEBUG, file)
+ ERRORS += 1
+ fd = open(SETNAME, "w")
+ data = "put-bytes going into {file}\n".format(file=file)
+ fd.write(data)
+ fd.close()
+ cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' set-bytes {sname}").format(osd=osd, pg=pg, json=JSON, sname=SETNAME)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.error("Bad exit status {ret} from set-bytes".format(ret=ret))
+ ERRORS += 1
+ fd = open(TESTNAME, "w")
+ cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' get-bytes -").format(osd=osd, pg=pg, json=JSON)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=fd)
+ fd.close()
+ if ret != 0:
+ logging.error("Bad exit status {ret} from get-bytes".format(ret=ret))
+ ERRORS += 1
+ cmd = "diff -q {setfile} {testfile}".format(setfile=SETNAME, testfile=TESTNAME)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.error("Data after set-bytes differ")
+ logging.debug("Got:")
+ cat_file(logging.DEBUG, TESTNAME)
+ logging.debug("Expected:")
+ cat_file(logging.DEBUG, SETNAME)
+ ERRORS += 1
+ fd = open(file, "r")
+ cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' set-bytes").format(osd=osd, pg=pg, json=JSON)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdin=fd)
+ if ret != 0:
+ logging.error("Bad exit status {ret} from set-bytes to restore object".format(ret=ret))
+ ERRORS += 1
+
+ try:
+ os.unlink(GETNAME)
+ except:
+ pass
+ try:
+ os.unlink(TESTNAME)
+ except:
+ pass
+ try:
+ os.unlink(SETNAME)
+ except:
+ pass
+
+ print "Test list-attrs get-attr"
+ ATTRFILE = r"/tmp/attrs.{pid}".format(pid=pid)
+ VALFILE = r"/tmp/val.{pid}".format(pid=pid)
+ for basename in db.keys():
+ file = os.path.join(DATADIR, basename)
+ JSON = [l for l in JSONOBJ if l.find("\"" + basename + "\"") != -1]
+ JSON = JSON[0]
+ for pg in OBJREPPGS:
+ OSDS = get_osds(pg, OSDDIR)
+ for osd in OSDS:
+ DIR = os.path.join(OSDDIR, os.path.join(osd, os.path.join("current", "{pg}_head".format(pg=pg))))
+ fname = [f for f in os.listdir(DIR) if os.path.isfile(os.path.join(DIR, f)) and string.find(f, basename + "_") == 0]
+ if not fname:
+ continue
+ fname = fname[0]
+ afd = open(ATTRFILE, "w")
+ cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' list-attrs").format(osd=osd, pg=pg, json=JSON)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=afd)
+ afd.close()
+ if ret != 0:
+ logging.error("list-attrs failed with {ret}".format(ret=ret))
+ ERRORS += 1
+ continue
+ keys = get_lines(ATTRFILE)
+ values = dict(db[basename]["xattr"])
+ for key in keys:
+ if key == "_" or key == "snapset":
+ continue
+ key = key.strip("_")
+ if key not in values:
+ logging.error("The key {key} should be present".format(key=key))
+ ERRORS += 1
+ continue
+ exp = values.pop(key)
+ vfd = open(VALFILE, "w")
+ cmd = (CFSD_PREFIX + "--pgid {pg} '{json}' get-attr {key}").format(osd=osd, pg=pg, json=JSON, key="_" + key)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=vfd)
+ vfd.close()
+ if ret != 0:
+ logging.error("get-attr failed with {ret}".format(ret=ret))
+ ERRORS += 1
+ continue
+ lines = get_lines(VALFILE)
+ val = lines[0]
+ if exp != val:
+ logging.error("For key {key} got value {got} instead of {expected}".format(key=key, got=val, expected=exp))
+ ERRORS += 1
+ if len(values) != 0:
+ logging.error("Not all keys found, remaining keys:")
+ print values
+
+ print "Test pg info"
+ for pg in ALLREPPGS + ALLECPGS:
+ for osd in get_osds(pg, OSDDIR):
+ cmd = (CFSD_PREFIX + "--type info --pgid {pg} | grep '\"pgid\": \"{pg}\"'").format(osd=osd, pg=pg)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=nullfd)
+ if ret != 0:
+ logging.error("Getting info failed for pg {pg} from {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
+ ERRORS += 1
+
+ print "Test pg logging"
+ for pg in ALLREPPGS + ALLECPGS:
+ for osd in get_osds(pg, OSDDIR):
+ tmpfd = open(TMPFILE, "w")
+ cmd = (CFSD_PREFIX + "--type log --pgid {pg}").format(osd=osd, pg=pg)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=tmpfd)
+ if ret != 0:
+ logging.error("Getting log failed for pg {pg} from {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
+ ERRORS += 1
+ HASOBJ = pg in OBJREPPGS + OBJECPGS
+ MODOBJ = False
+ for line in get_lines(TMPFILE):
+ if line.find("modify") != -1:
+ MODOBJ = True
+ break
+ if HASOBJ != MODOBJ:
+ logging.error("Bad log for pg {pg} from {osd}".format(pg=pg, osd=osd))
+ MSG = (HASOBJ and [""] or ["NOT "])[0]
+ print "Log should {msg}have a modify entry".format(msg=MSG)
+ ERRORS += 1
+
+ try:
+ os.unlink(TMPFILE)
+ except:
+ pass
+
+ print "Test pg export"
+ EXP_ERRORS = 0
+ os.mkdir(TESTDIR)
+ for osd in [f for f in os.listdir(OSDDIR) if os.path.isdir(os.path.join(OSDDIR, f)) and string.find(f, "osd") == 0]:
+ os.mkdir(os.path.join(TESTDIR, osd))
+ for pg in ALLREPPGS + ALLECPGS:
+ for osd in get_osds(pg, OSDDIR):
+ mydir = os.path.join(TESTDIR, osd)
+ fname = os.path.join(mydir, pg)
+ cmd = (CFSD_PREFIX + "--type export --pgid {pg} --file {file}").format(osd=osd, pg=pg, file=fname)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+ if ret != 0:
+ logging.error("Exporting failed for pg {pg} on {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
+ EXP_ERRORS += 1
+
+ ERRORS += EXP_ERRORS
+
+ print "Test pg removal"
+ RM_ERRORS = 0
+ for pg in ALLREPPGS + ALLECPGS:
+ for osd in get_osds(pg, OSDDIR):
+ cmd = (CFSD_PREFIX + "--type remove --pgid {pg}").format(pg=pg, osd=osd)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=nullfd)
+ if ret != 0:
+ logging.error("Removing failed for pg {pg} on {osd} with {ret}".format(pg=pg, osd=osd, ret=ret))
+ RM_ERRORS += 1
+
+ ERRORS += RM_ERRORS
+
+ IMP_ERRORS = 0
+ if EXP_ERRORS == 0 and RM_ERRORS == 0:
+ print "Test pg import"
+ for osd in [f for f in os.listdir(OSDDIR) if os.path.isdir(os.path.join(OSDDIR, f)) and string.find(f, "osd") == 0]:
+ dir = os.path.join(TESTDIR, osd)
+ for pg in [f for f in os.listdir(dir) if os.path.isfile(os.path.join(dir, f))]:
+ file = os.path.join(dir, pg)
+ cmd = (CFSD_PREFIX + "--type import --file {file}").format(osd=osd, file=file)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=nullfd)
+ if ret != 0:
+ logging.error("Import failed from {file} with {ret}".format(file=file, ret=ret))
+ IMP_ERRORS += 1
+ else:
+ logging.warning("SKIPPING IMPORT TESTS DUE TO PREVIOUS FAILURES")
+
+ ERRORS += IMP_ERRORS
+ logging.debug(cmd)
+ call("/bin/rm -rf {dir}".format(dir=TESTDIR), shell=True)
+
+ if EXP_ERRORS == 0 and RM_ERRORS == 0 and IMP_ERRORS == 0:
+ print "Verify replicated import data"
+ for file in [f for f in os.listdir(DATADIR) if f.find(REP_NAME) == 0]:
+ path = os.path.join(DATADIR, file)
+ tmpfd = open(TMPFILE, "w")
+ cmd = "find {dir} -name '{file}_*'".format(dir=OSDDIR, file=file)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True, stdout=tmpfd)
+ if ret:
+ logging.critical("INTERNAL ERROR")
+ sys.exit(1)
+ tmpfd.close()
+ obj_locs = get_lines(TMPFILE)
+ if len(obj_locs) == 0:
+ logging.error("Can't find imported object {name}".format(name=file))
+ ERRORS += 1
+ for obj_loc in obj_locs:
+ cmd = "diff -q {src} {obj_loc}".format(src=path, obj_loc=obj_loc)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.error("{file} data not imported properly into {obj}".format(file=file, obj=obj_loc))
+ ERRORS += 1
+
+ vstart(new=False)
+ wait_for_health()
+
+ print "Verify erasure coded import data"
+ for file in [f for f in os.listdir(DATADIR) if f.find(EC_NAME) == 0]:
+ path = os.path.join(DATADIR, file)
+ try:
+ os.unlink(TMPFILE)
+ except:
+ pass
+ cmd = "./rados -p {pool} get {file} {out}".format(pool=EC_POOL, file=file, out=TMPFILE)
+ logging.debug(cmd)
+ call(cmd, shell=True, stdout=nullfd, stderr=nullfd)
+ cmd = "diff -q {src} {result}".format(src=path, result=TMPFILE)
+ logging.debug(cmd)
+ ret = call(cmd, shell=True)
+ if ret != 0:
+ logging.error("{file} data not imported properly".format(file=file))
+ ERRORS += 1
+ try:
+ os.unlink(TMPFILE)
+ except:
+ pass
+
+ call("./stop.sh", stderr=nullfd)
+ else:
+ logging.warning("SKIPPING CHECKING IMPORT DATA DUE TO PREVIOUS FAILURES")
+
+ call("/bin/rm -rf {dir}".format(dir=DATADIR), shell=True)
+
+ if ERRORS == 0:
+ print "TEST PASSED"
+ sys.exit(0)
+ else:
+ print "TEST FAILED WITH {errcount} ERRORS".format(errcount=ERRORS)
+ sys.exit(1)
+
+if __name__ == "__main__":
+ main()
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2013 Inktank
- *
- * This is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1, as published by the Free Software
- * Foundation. See file COPYING.
- *
- */
-
-#include <boost/program_options/variables_map.hpp>
-#include <boost/program_options/parsers.hpp>
-
-#include <stdlib.h>
-
-#include "common/Formatter.h"
-#include "common/errno.h"
-
-#include "global/global_init.h"
-
-#include "os/ObjectStore.h"
-#include "os/FileStore.h"
-
-#include "osd/PGLog.h"
-#include "osd/OSD.h"
-
-#include "json_spirit/json_spirit_value.h"
-#include "json_spirit/json_spirit_reader.h"
-
-namespace po = boost::program_options;
-using namespace std;
-
-static coll_t META_COLL("meta");
-
-enum {
- TYPE_NONE = 0,
- TYPE_PG_BEGIN,
- TYPE_PG_END,
- TYPE_OBJECT_BEGIN,
- TYPE_OBJECT_END,
- TYPE_DATA,
- TYPE_ATTRS,
- TYPE_OMAP_HDR,
- TYPE_OMAP,
- TYPE_PG_METADATA,
- END_OF_TYPES, //Keep at the end
-};
-
-//#define INTERNAL_TEST
-//#define INTERNAL_TEST2
-
-#ifdef INTERNAL_TEST
-CompatSet get_test_compat_set() {
- CompatSet::FeatureSet ceph_osd_feature_compat;
- CompatSet::FeatureSet ceph_osd_feature_ro_compat;
- CompatSet::FeatureSet ceph_osd_feature_incompat;
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_BASE);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_PGINFO);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_OLOC);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEC);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_CATEGORIES);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_HOBJECTPOOL);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_BIGINFO);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEVELDBINFO);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEVELDBLOG);
-#ifdef INTERNAL_TEST2
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SNAPMAPPER);
- ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SHARDS);
-#endif
- return CompatSet(ceph_osd_feature_compat, ceph_osd_feature_ro_compat,
- ceph_osd_feature_incompat);
-}
-#endif
-
-typedef uint8_t sectiontype_t;
-typedef uint32_t mymagic_t;
-typedef int64_t mysize_t;
-const ssize_t max_read = 1024 * 1024;
-const uint16_t shortmagic = 0xffce; //goes into stream as "ceff"
-//endmagic goes into stream as "ceff ffec"
-const mymagic_t endmagic = (0xecff << 16) | shortmagic;
-const int fd_none = INT_MIN;
-bool outistty;
-
-//The first FIXED_LENGTH bytes are a fixed
-//portion of the export output. This includes the overall
-//version number, and size of header and footer.
-//THIS STRUCTURE CAN ONLY BE APPENDED TO. If it needs to expand,
-//the version can be bumped and then anything
-//can be added to the export format.
-struct super_header {
- static const uint32_t super_magic = (shortmagic << 16) | shortmagic;
- static const uint32_t super_ver = 2;
- static const uint32_t FIXED_LENGTH = 16;
- uint32_t magic;
- uint32_t version;
- uint32_t header_size;
- uint32_t footer_size;
-
- super_header() : magic(0), version(0), header_size(0), footer_size(0) { }
- int read_super();
-
- void encode(bufferlist& bl) const {
- ::encode(magic, bl);
- ::encode(version, bl);
- ::encode(header_size, bl);
- ::encode(footer_size, bl);
- }
- void decode(bufferlist::iterator& bl) {
- ::decode(magic, bl);
- ::decode(version, bl);
- ::decode(header_size, bl);
- ::decode(footer_size, bl);
- }
-};
-
-struct header {
- sectiontype_t type;
- mysize_t size;
- header(sectiontype_t type, mysize_t size) :
- type(type), size(size) { }
- header(): type(0), size(0) { }
-
- int get_header();
-
- void encode(bufferlist& bl) const {
- uint32_t debug_type = (type << 24) | (type << 16) | shortmagic;
- ENCODE_START(1, 1, bl);
- ::encode(debug_type, bl);
- ::encode(size, bl);
- ENCODE_FINISH(bl);
- }
- void decode(bufferlist::iterator& bl) {
- uint32_t debug_type;
- DECODE_START(1, bl);
- ::decode(debug_type, bl);
- type = debug_type >> 24;
- ::decode(size, bl);
- DECODE_FINISH(bl);
- }
-};
-
-struct footer {
- mymagic_t magic;
- footer() : magic(endmagic) { }
-
- int get_footer();
-
- void encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
- ::encode(magic, bl);
- ENCODE_FINISH(bl);
- }
- void decode(bufferlist::iterator& bl) {
- DECODE_START(1, bl);
- ::decode(magic, bl);
- DECODE_FINISH(bl);
- }
-};
-
-struct pg_begin {
- spg_t pgid;
- OSDSuperblock superblock;
-
- pg_begin(spg_t pg, const OSDSuperblock& sb):
- pgid(pg), superblock(sb) { }
- pg_begin() { }
-
- void encode(bufferlist& bl) const {
- // If superblock doesn't include CEPH_FS_FEATURE_INCOMPAT_SHARDS then
- // shard will be NO_SHARD for a replicated pool. This means
- // that we allow the decode by struct_v 2.
- ENCODE_START(3, 2, bl);
- ::encode(pgid.pgid, bl);
- ::encode(superblock, bl);
- ::encode(pgid.shard, bl);
- ENCODE_FINISH(bl);
- }
- // NOTE: New super_ver prevents decode from ver 1
- void decode(bufferlist::iterator& bl) {
- DECODE_START(3, bl);
- ::decode(pgid.pgid, bl);
- if (struct_v > 1) {
- ::decode(superblock, bl);
- }
- if (struct_v > 2) {
- ::decode(pgid.shard, bl);
- } else {
- pgid.shard = shard_id_t::NO_SHARD;
- }
- DECODE_FINISH(bl);
- }
-};
-
-struct object_begin {
- ghobject_t hoid;
- object_begin(const ghobject_t &hoid): hoid(hoid) { }
- object_begin() { }
-
- // If superblock doesn't include CEPH_FS_FEATURE_INCOMPAT_SHARDS then
- // generation will be NO_GEN, shard_id will be NO_SHARD for a replicated
- // pool. This means we will allow the decode by struct_v 1.
- void encode(bufferlist& bl) const {
- ENCODE_START(2, 1, bl);
- ::encode(hoid.hobj, bl);
- ::encode(hoid.generation, bl);
- ::encode(hoid.shard_id, bl);
- ENCODE_FINISH(bl);
- }
- void decode(bufferlist::iterator& bl) {
- DECODE_START(2, bl);
- ::decode(hoid.hobj, bl);
- if (struct_v > 1) {
- ::decode(hoid.generation, bl);
- ::decode(hoid.shard_id, bl);
- } else {
- hoid.generation = ghobject_t::NO_GEN;
- hoid.shard_id = shard_id_t::NO_SHARD;
- }
- DECODE_FINISH(bl);
- }
-};
-
-struct data_section {
- uint64_t offset;
- uint64_t len;
- bufferlist databl;
- data_section(uint64_t offset, uint64_t len, bufferlist bl):
- offset(offset), len(len), databl(bl) { }
- data_section(): offset(0), len(0) { }
-
- void encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
- ::encode(offset, bl);
- ::encode(len, bl);
- ::encode(databl, bl);
- ENCODE_FINISH(bl);
- }
- void decode(bufferlist::iterator& bl) {
- DECODE_START(1, bl);
- ::decode(offset, bl);
- ::decode(len, bl);
- ::decode(databl, bl);
- DECODE_FINISH(bl);
- }
-};
-
-struct attr_section {
- map<string,bufferptr> data;
- attr_section(const map<string,bufferptr> &data) : data(data) { }
- attr_section() { }
-
- void encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
- ::encode(data, bl);
- ENCODE_FINISH(bl);
- }
- void decode(bufferlist::iterator& bl) {
- DECODE_START(1, bl);
- ::decode(data, bl);
- DECODE_FINISH(bl);
- }
-};
-
-struct omap_hdr_section {
- bufferlist hdr;
- omap_hdr_section(bufferlist hdr) : hdr(hdr) { }
- omap_hdr_section() { }
-
- void encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
- ::encode(hdr, bl);
- ENCODE_FINISH(bl);
- }
- void decode(bufferlist::iterator& bl) {
- DECODE_START(1, bl);
- ::decode(hdr, bl);
- DECODE_FINISH(bl);
- }
-};
-
-struct omap_section {
- map<string, bufferlist> omap;
- omap_section(const map<string, bufferlist> &omap) :
- omap(omap) { }
- omap_section() { }
-
- void encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
- ::encode(omap, bl);
- ENCODE_FINISH(bl);
- }
- void decode(bufferlist::iterator& bl) {
- DECODE_START(1, bl);
- ::decode(omap, bl);
- DECODE_FINISH(bl);
- }
-};
-
-struct metadata_section {
- __u8 struct_ver;
- epoch_t map_epoch;
- pg_info_t info;
- pg_log_t log;
-
- metadata_section(__u8 struct_ver, epoch_t map_epoch, const pg_info_t &info,
- const pg_log_t &log)
- : struct_ver(struct_ver),
- map_epoch(map_epoch),
- info(info),
- log(log) { }
- metadata_section()
- : struct_ver(0),
- map_epoch(0) { }
-
- void encode(bufferlist& bl) const {
- ENCODE_START(1, 1, bl);
- ::encode(struct_ver, bl);
- ::encode(map_epoch, bl);
- ::encode(info, bl);
- ::encode(log, bl);
- ENCODE_FINISH(bl);
- }
- void decode(bufferlist::iterator& bl) {
- DECODE_START(1, bl);
- ::decode(struct_ver, bl);
- ::decode(map_epoch, bl);
- ::decode(info, bl);
- ::decode(log, bl);
- DECODE_FINISH(bl);
- }
-};
-
-hobject_t infos_oid = OSD::make_infos_oid();
-hobject_t biginfo_oid, log_oid;
-
-int file_fd = fd_none;
-bool debug = false;
-super_header sh;
-
-template <typename T>
-int write_section(sectiontype_t type, const T& obj, int fd) {
- bufferlist blhdr, bl, blftr;
- obj.encode(bl);
- header hdr(type, bl.length());
- hdr.encode(blhdr);
- footer ft;
- ft.encode(blftr);
-
- int ret = blhdr.write_fd(fd);
- if (ret) return ret;
- ret = bl.write_fd(fd);
- if (ret) return ret;
- ret = blftr.write_fd(fd);
- return ret;
-}
-
-// Convert non-printable characters to '\###'
-static void cleanbin(string &str)
-{
- bool cleaned = false;
- string clean;
-
- for (string::iterator it = str.begin(); it != str.end(); ++it) {
- if (!isprint(*it)) {
- clean.push_back('\\');
- clean.push_back('0' + ((*it >> 6) & 7));
- clean.push_back('0' + ((*it >> 3) & 7));
- clean.push_back('0' + (*it & 7));
- cleaned = true;
- } else {
- clean.push_back(*it);
- }
- }
-
- if (cleaned)
- str = clean;
- return;
-}
-
-int write_simple(sectiontype_t type, int fd)
-{
- bufferlist hbl;
-
- header hdr(type, 0);
- hdr.encode(hbl);
- return hbl.write_fd(fd);
-}
-
-static int get_fd_data(int fd, bufferlist &bl)
-{
- uint64_t total = 0;
- do {
- ssize_t bytes = bl.read_fd(fd, max_read);
- if (bytes < 0) {
- cerr << "read_fd error " << cpp_strerror(-bytes) << std::endl;
- return 1;
- }
-
- if (bytes == 0)
- break;
-
- total += bytes;
- } while(true);
-
- assert(bl.length() == total);
- return 0;
-}
-
-static void invalid_path(string &path)
-{
- cerr << "Invalid path to osd store specified: " << path << "\n";
- exit(1);
-}
-
-int get_log(ObjectStore *fs, coll_t coll, spg_t pgid, const pg_info_t &info,
- PGLog::IndexedLog &log, pg_missing_t &missing)
-{
- map<eversion_t, hobject_t> divergent_priors;
- try {
- ostringstream oss;
- PGLog::read_log(fs, coll, log_oid, info, divergent_priors, log, missing, oss);
- if (debug && oss.str().size())
- cerr << oss.str() << std::endl;
- }
- catch (const buffer::error &e) {
- cerr << "read_log threw exception error " << e.what() << std::endl;
- return 1;
- }
- return 0;
-}
-
-//Based on RemoveWQ::_process()
-void remove_coll(ObjectStore *store, const coll_t &coll)
-{
- spg_t pg;
- coll.is_pg_prefix(pg);
- OSDriver driver(
- store,
- coll_t(),
- OSD::make_snapmapper_oid());
- SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
-
- vector<ghobject_t> objects;
- ghobject_t next;
- int r = 0;
- int64_t num = 0;
- ObjectStore::Transaction *t = new ObjectStore::Transaction;
- cout << "remove_coll " << coll << std::endl;
- while (!next.is_max()) {
- r = store->collection_list_partial(coll, next, 200, 300, 0,
- &objects, &next);
- if (r < 0)
- goto out;
- for (vector<ghobject_t>::iterator i = objects.begin();
- i != objects.end();
- ++i, ++num) {
-
- OSDriver::OSTransaction _t(driver.get_transaction(t));
- cout << "remove " << *i << std::endl;
- int r = mapper.remove_oid(i->hobj, &_t);
- if (r != 0 && r != -ENOENT) {
- assert(0);
- }
-
- t->remove(coll, *i);
- if (num >= 30) {
- store->apply_transaction(*t);
- delete t;
- t = new ObjectStore::Transaction;
- num = 0;
- }
- }
- }
- t->remove_collection(coll);
- store->apply_transaction(*t);
-out:
- delete t;
-}
-
-//Based on part of OSD::load_pgs()
-int finish_remove_pgs(ObjectStore *store, uint64_t *next_removal_seq)
-{
- vector<coll_t> ls;
- int r = store->list_collections(ls);
- if (r < 0) {
- cerr << "finish_remove_pgs: failed to list pgs: " << cpp_strerror(-r)
- << std::endl;
- return r;
- }
-
- for (vector<coll_t>::iterator it = ls.begin();
- it != ls.end();
- ++it) {
- spg_t pgid;
- snapid_t snap;
-
- if (it->is_temp(pgid)) {
- cout << "finish_remove_pgs " << *it << " clearing temp" << std::endl;
- OSD::recursive_remove_collection(store, *it);
- continue;
- }
-
- if (it->is_pg(pgid, snap)) {
- continue;
- }
-
- uint64_t seq;
- if (it->is_removal(&seq, &pgid)) {
- if (seq >= *next_removal_seq)
- *next_removal_seq = seq + 1;
- cout << "finish_remove_pgs removing " << *it << ", seq is "
- << seq << " pgid is " << pgid << std::endl;
- remove_coll(store, *it);
- continue;
- }
-
- //cout << "finish_remove_pgs ignoring unrecognized " << *it << std::endl;
- }
- return 0;
-}
-
-int initiate_new_remove_pg(ObjectStore *store, spg_t r_pgid,
- uint64_t *next_removal_seq)
-{
- ObjectStore::Transaction *rmt = new ObjectStore::Transaction;
-
- if (store->collection_exists(coll_t(r_pgid))) {
- coll_t to_remove = coll_t::make_removal_coll((*next_removal_seq)++, r_pgid);
- cout << "collection rename " << coll_t(r_pgid)
- << " to " << to_remove
- << std::endl;
- rmt->collection_rename(coll_t(r_pgid), to_remove);
- } else {
- delete rmt;
- return ENOENT;
- }
-
- cout << "remove " << META_COLL << " " << log_oid.oid << std::endl;
- rmt->remove(META_COLL, log_oid);
- cout << "remove " << META_COLL << " " << biginfo_oid.oid << std::endl;
- rmt->remove(META_COLL, biginfo_oid);
-
- store->apply_transaction(*rmt);
-
- return 0;
-}
-
-int header::get_header()
-{
- bufferlist ebl;
- bufferlist::iterator ebliter = ebl.begin();
- ssize_t bytes;
-
- bytes = ebl.read_fd(file_fd, sh.header_size);
- if ((size_t)bytes != sh.header_size) {
- cerr << "Unexpected EOF" << std::endl;
- return EFAULT;
- }
-
- decode(ebliter);
-
- return 0;
-}
-
-int footer::get_footer()
-{
- bufferlist ebl;
- bufferlist::iterator ebliter = ebl.begin();
- ssize_t bytes;
-
- bytes = ebl.read_fd(file_fd, sh.footer_size);
- if ((size_t)bytes != sh.footer_size) {
- cerr << "Unexpected EOF" << std::endl;
- return EFAULT;
- }
-
- decode(ebliter);
-
- if (magic != endmagic) {
- cerr << "Bad footer magic" << std::endl;
- return EFAULT;
- }
-
- return 0;
-}
-
-int write_info(ObjectStore::Transaction &t, epoch_t epoch, pg_info_t &info,
- __u8 struct_ver)
-{
- //Empty for this
- interval_set<snapid_t> snap_collections; // obsolete
- map<epoch_t,pg_interval_t> past_intervals;
- coll_t coll(info.pgid);
-
- int ret = PG::_write_info(t, epoch,
- info, coll,
- past_intervals,
- snap_collections,
- infos_oid,
- struct_ver,
- true, true);
- if (ret < 0) ret = -ret;
- if (ret) cerr << "Failed to write info" << std::endl;
- return ret;
-}
-
-void write_log(ObjectStore::Transaction &t, pg_log_t &log)
-{
- map<eversion_t, hobject_t> divergent_priors;
- PGLog::write_log(t, log, log_oid, divergent_priors);
-}
-
-int write_pg(ObjectStore::Transaction &t, epoch_t epoch, pg_info_t &info,
- pg_log_t &log, __u8 struct_ver)
-{
- int ret = write_info(t, epoch, info, struct_ver);
- if (ret) return ret;
- write_log(t, log);
- return 0;
-}
-
-const int OMAP_BATCH_SIZE = 25;
-void get_omap_batch(ObjectMap::ObjectMapIterator &iter, map<string, bufferlist> &oset)
-{
- oset.clear();
- for (int count = OMAP_BATCH_SIZE; count && iter->valid(); --count, iter->next()) {
- oset.insert(pair<string, bufferlist>(iter->key(), iter->value()));
- }
-}
-
-int export_file(ObjectStore *store, coll_t cid, ghobject_t &obj)
-{
- struct stat st;
- mysize_t total;
- footer ft;
-
- int ret = store->stat(cid, obj, &st);
- if (ret < 0)
- return ret;
-
- cerr << "read " << obj << std::endl;
-
- total = st.st_size;
- if (debug)
- cerr << "size=" << total << std::endl;
-
- object_begin objb(obj);
- ret = write_section(TYPE_OBJECT_BEGIN, objb, file_fd);
- if (ret < 0)
- return ret;
-
- uint64_t offset = 0;
- bufferlist rawdatabl;
- while(total > 0) {
- rawdatabl.clear();
- mysize_t len = max_read;
- if (len > total)
- len = total;
-
- ret = store->read(cid, obj, offset, len, rawdatabl);
- if (ret < 0)
- return ret;
- if (ret == 0)
- return -EINVAL;
-
- data_section dblock(offset, len, rawdatabl);
- if (debug)
- cerr << "data section offset=" << offset << " len=" << len << std::endl;
-
- total -= ret;
- offset += ret;
-
- ret = write_section(TYPE_DATA, dblock, file_fd);
- if (ret) return ret;
- }
-
- //Handle attrs for this object
- map<string,bufferptr> aset;
- ret = store->getattrs(cid, obj, aset);
- if (ret) return ret;
- attr_section as(aset);
- ret = write_section(TYPE_ATTRS, as, file_fd);
- if (ret)
- return ret;
-
- if (debug) {
- cerr << "attrs size " << aset.size() << std::endl;
- }
-
- //Handle omap information
- bufferlist hdrbuf;
- ret = store->omap_get_header(cid, obj, &hdrbuf, true);
- if (ret < 0) {
- cerr << "omap_get_header: " << cpp_strerror(-ret) << std::endl;
- return ret;
- }
-
- omap_hdr_section ohs(hdrbuf);
- ret = write_section(TYPE_OMAP_HDR, ohs, file_fd);
- if (ret)
- return ret;
-
- ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(cid, obj);
- if (!iter) {
- ret = -ENOENT;
- cerr << "omap_get_iterator: " << cpp_strerror(-ret) << std::endl;
- return ret;
- }
- iter->seek_to_first();
- int mapcount = 0;
- map<string, bufferlist> out;
- while(iter->valid()) {
- get_omap_batch(iter, out);
-
- if (out.empty()) break;
-
- mapcount += out.size();
- omap_section oms(out);
- ret = write_section(TYPE_OMAP, oms, file_fd);
- if (ret)
- return ret;
- }
- if (debug)
- cerr << "omap map size " << mapcount << std::endl;
-
- ret = write_simple(TYPE_OBJECT_END, file_fd);
- if (ret)
- return ret;
-
- return 0;
-}
-
-int export_files(ObjectStore *store, coll_t coll)
-{
- vector<ghobject_t> objects;
- ghobject_t next;
-
- while (!next.is_max()) {
- int r = store->collection_list_partial(coll, next, 200, 300, 0,
- &objects, &next);
- if (r < 0)
- return r;
- for (vector<ghobject_t>::iterator i = objects.begin();
- i != objects.end();
- ++i) {
- r = export_file(store, coll, *i);
- if (r < 0)
- return r;
- }
- }
- return 0;
-}
-
-//Write super_header with its fixed 16 byte length
-void write_super()
-{
- bufferlist superbl;
- super_header sh;
- footer ft;
-
- header hdr(TYPE_NONE, 0);
- hdr.encode(superbl);
-
- sh.magic = super_header::super_magic;
- sh.version = super_header::super_ver;
- sh.header_size = superbl.length();
- superbl.clear();
- ft.encode(superbl);
- sh.footer_size = superbl.length();
- superbl.clear();
-
- sh.encode(superbl);
- assert(super_header::FIXED_LENGTH == superbl.length());
- superbl.write_fd(file_fd);
-}
-
-int do_export(ObjectStore *fs, coll_t coll, spg_t pgid, pg_info_t &info,
- epoch_t map_epoch, __u8 struct_ver, const OSDSuperblock& superblock)
-{
- PGLog::IndexedLog log;
- pg_missing_t missing;
-
- cerr << "Exporting " << pgid << std::endl;
-
- int ret = get_log(fs, coll, pgid, info, log, missing);
- if (ret > 0)
- return ret;
-
- write_super();
-
- pg_begin pgb(pgid, superblock);
- ret = write_section(TYPE_PG_BEGIN, pgb, file_fd);
- if (ret)
- return ret;
-
- ret = export_files(fs, coll);
- if (ret) {
- cerr << "export_files error " << ret << std::endl;
- return ret;
- }
-
- metadata_section ms(struct_ver, map_epoch, info, log);
- ret = write_section(TYPE_PG_METADATA, ms, file_fd);
- if (ret)
- return ret;
-
- ret = write_simple(TYPE_PG_END, file_fd);
- if (ret)
- return ret;
-
- return 0;
-}
-
-int super_header::read_super()
-{
- bufferlist ebl;
- bufferlist::iterator ebliter = ebl.begin();
- ssize_t bytes;
-
- bytes = ebl.read_fd(file_fd, super_header::FIXED_LENGTH);
- if ((size_t)bytes != super_header::FIXED_LENGTH) {
- cerr << "Unexpected EOF" << std::endl;
- return EFAULT;
- }
-
- decode(ebliter);
-
- return 0;
-}
-
-int read_section(int fd, sectiontype_t *type, bufferlist *bl)
-{
- header hdr;
- ssize_t bytes;
-
- int ret = hdr.get_header();
- if (ret)
- return ret;
-
- *type = hdr.type;
-
- bl->clear();
- bytes = bl->read_fd(fd, hdr.size);
- if (bytes != hdr.size) {
- cerr << "Unexpected EOF" << std::endl;
- return EFAULT;
- }
-
- if (hdr.size > 0) {
- footer ft;
- ret = ft.get_footer();
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-int get_data(ObjectStore *store, coll_t coll, ghobject_t hoid,
- ObjectStore::Transaction *t, bufferlist &bl)
-{
- bufferlist::iterator ebliter = bl.begin();
- data_section ds;
- ds.decode(ebliter);
-
- if (debug)
- cerr << "\tdata: offset " << ds.offset << " len " << ds.len << std::endl;
- t->write(coll, hoid, ds.offset, ds.len, ds.databl);
- return 0;
-}
-
-int get_attrs(ObjectStore *store, coll_t coll, ghobject_t hoid,
- ObjectStore::Transaction *t, bufferlist &bl,
- OSDriver &driver, SnapMapper &snap_mapper)
-{
- bufferlist::iterator ebliter = bl.begin();
- attr_section as;
- as.decode(ebliter);
-
- if (debug)
- cerr << "\tattrs: len " << as.data.size() << std::endl;
- t->setattrs(coll, hoid, as.data);
-
- if (hoid.hobj.snap < CEPH_MAXSNAP && hoid.generation == ghobject_t::NO_GEN) {
- map<string,bufferptr>::iterator mi = as.data.find(OI_ATTR);
- if (mi != as.data.end()) {
- bufferlist attr_bl;
- attr_bl.push_back(mi->second);
- object_info_t oi(attr_bl);
-
- if (debug)
- cerr << "object_info " << oi << std::endl;
-
- OSDriver::OSTransaction _t(driver.get_transaction(t));
- set<snapid_t> oi_snaps(oi.snaps.begin(), oi.snaps.end());
- snap_mapper.add_oid(hoid.hobj, oi_snaps, &_t);
- }
- }
-
- return 0;
-}
-
-int get_omap_hdr(ObjectStore *store, coll_t coll, ghobject_t hoid,
- ObjectStore::Transaction *t, bufferlist &bl)
-{
- bufferlist::iterator ebliter = bl.begin();
- omap_hdr_section oh;
- oh.decode(ebliter);
-
- if (debug)
- cerr << "\tomap header: " << string(oh.hdr.c_str(), oh.hdr.length())
- << std::endl;
- t->omap_setheader(coll, hoid, oh.hdr);
- return 0;
-}
-
-int get_omap(ObjectStore *store, coll_t coll, ghobject_t hoid,
- ObjectStore::Transaction *t, bufferlist &bl)
-{
- bufferlist::iterator ebliter = bl.begin();
- omap_section os;
- os.decode(ebliter);
-
- if (debug)
- cerr << "\tomap: size " << os.omap.size() << std::endl;
- t->omap_setkeys(coll, hoid, os.omap);
- return 0;
-}
-
-int get_object(ObjectStore *store, coll_t coll, bufferlist &bl)
-{
- ObjectStore::Transaction tran;
- ObjectStore::Transaction *t = &tran;
- bufferlist::iterator ebliter = bl.begin();
- object_begin ob;
- ob.decode(ebliter);
- OSDriver driver(
- store,
- coll_t(),
- OSD::make_snapmapper_oid());
- spg_t pg;
- coll.is_pg_prefix(pg);
- SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
-
- t->touch(coll, ob.hoid);
-
- cout << "Write " << ob.hoid << std::endl;
-
- bufferlist ebl;
- bool done = false;
- while(!done) {
- sectiontype_t type;
- int ret = read_section(file_fd, &type, &ebl);
- if (ret)
- return ret;
-
- //cout << "\tdo_object: Section type " << hex << type << dec << std::endl;
- //cout << "\t\tsection size " << ebl.length() << std::endl;
- if (type >= END_OF_TYPES) {
- cout << "Skipping unknown object section type" << std::endl;
- continue;
- }
- switch(type) {
- case TYPE_DATA:
- ret = get_data(store, coll, ob.hoid, t, ebl);
- if (ret) return ret;
- break;
- case TYPE_ATTRS:
- ret = get_attrs(store, coll, ob.hoid, t, ebl, driver, mapper);
- if (ret) return ret;
- break;
- case TYPE_OMAP_HDR:
- ret = get_omap_hdr(store, coll, ob.hoid, t, ebl);
- if (ret) return ret;
- break;
- case TYPE_OMAP:
- ret = get_omap(store, coll, ob.hoid, t, ebl);
- if (ret) return ret;
- break;
- case TYPE_OBJECT_END:
- done = true;
- break;
- default:
- return EFAULT;
- }
- }
- store->apply_transaction(*t);
- return 0;
-}
-
-int get_pg_metadata(ObjectStore *store, coll_t coll, bufferlist &bl)
-{
- ObjectStore::Transaction tran;
- ObjectStore::Transaction *t = &tran;
- bufferlist::iterator ebliter = bl.begin();
- metadata_section ms;
- ms.decode(ebliter);
-
-#if DIAGNOSTIC
- Formatter *formatter = new JSONFormatter(true);
- cout << "struct_v " << (int)ms.struct_ver << std::endl;
- cout << "epoch " << ms.map_epoch << std::endl;
- formatter->open_object_section("info");
- ms.info.dump(formatter);
- formatter->close_section();
- formatter->flush(cout);
- cout << std::endl;
-
- formatter->open_object_section("log");
- ms.log.dump(formatter);
- formatter->close_section();
- formatter->flush(cout);
- cout << std::endl;
-#endif
-
- coll_t newcoll(ms.info.pgid);
- t->collection_rename(coll, newcoll);
-
- int ret = write_pg(*t, ms.map_epoch, ms.info, ms.log, ms.struct_ver);
- if (ret) return ret;
-
- store->apply_transaction(*t);
-
- return 0;
-}
-
-int do_import(ObjectStore *store, OSDSuperblock& sb)
-{
- bufferlist ebl;
- pg_info_t info;
- PGLog::IndexedLog log;
-
- uint64_t next_removal_seq = 0; //My local seq
- finish_remove_pgs(store, &next_removal_seq);
-
- int ret = sh.read_super();
- if (ret)
- return ret;
-
- if (sh.magic != super_header::super_magic) {
- cerr << "Invalid magic number" << std::endl;
- return EFAULT;
- }
-
- if (sh.version > super_header::super_ver) {
- cerr << "Can't handle export format version=" << sh.version << std::endl;
- return EINVAL;
- }
-
- //First section must be TYPE_PG_BEGIN
- sectiontype_t type;
- ret = read_section(file_fd, &type, &ebl);
- if (ret)
- return ret;
- if (type != TYPE_PG_BEGIN) {
- return EFAULT;
- }
-
- bufferlist::iterator ebliter = ebl.begin();
- pg_begin pgb;
- pgb.decode(ebliter);
- spg_t pgid = pgb.pgid;
-
- if (debug) {
- cerr << "Exported features: " << pgb.superblock.compat_features << std::endl;
- }
- if (sb.compat_features.compare(pgb.superblock.compat_features) == -1) {
- cerr << "Export has incompatible features set "
- << pgb.superblock.compat_features << std::endl;
- return 1;
- }
-
- log_oid = OSD::make_pg_log_oid(pgid);
- biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
-
- //Check for PG already present.
- coll_t coll(pgid);
- if (store->collection_exists(coll)) {
- cerr << "pgid " << pgid << " already exists" << std::endl;
- return 1;
- }
-
- //Switch to collection which will be removed automatically if
- //this program is interupted.
- coll_t rmcoll = coll_t::make_removal_coll(next_removal_seq, pgid);
- ObjectStore::Transaction *t = new ObjectStore::Transaction;
- t->create_collection(rmcoll);
- store->apply_transaction(*t);
- delete t;
-
- cout << "Importing pgid " << pgid << std::endl;
-
- bool done = false;
- bool found_metadata = false;
- while(!done) {
- ret = read_section(file_fd, &type, &ebl);
- if (ret)
- return ret;
-
- //cout << "do_import: Section type " << hex << type << dec << std::endl;
- if (type >= END_OF_TYPES) {
- cout << "Skipping unknown section type" << std::endl;
- continue;
- }
- switch(type) {
- case TYPE_OBJECT_BEGIN:
- ret = get_object(store, rmcoll, ebl);
- if (ret) return ret;
- break;
- case TYPE_PG_METADATA:
- ret = get_pg_metadata(store, rmcoll, ebl);
- if (ret) return ret;
- found_metadata = true;
- break;
- case TYPE_PG_END:
- done = true;
- break;
- default:
- return EFAULT;
- }
- }
-
- if (!found_metadata) {
- cerr << "Missing metadata section" << std::endl;
- return EFAULT;
- }
-
- return 0;
-}
-
-int do_list(ObjectStore *store, coll_t coll, Formatter *formatter)
-{
- vector<ghobject_t> objects;
- ghobject_t next;
- while (!next.is_max()) {
- int r = store->collection_list_partial(coll, next, 200, 300, 0,
- &objects, &next);
- if (r < 0)
- return r;
- for (vector<ghobject_t>::iterator i = objects.begin();
- i != objects.end(); ++i) {
-
- formatter->open_object_section("list");
- i->dump(formatter);
- formatter->close_section();
- formatter->flush(cout);
- cout << std::endl;
- }
- }
- return 0;
-}
-
-int do_remove_object(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
-{
- spg_t pg;
- coll.is_pg_prefix(pg);
- OSDriver driver(
- store,
- coll_t(),
- OSD::make_snapmapper_oid());
- SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
- struct stat st;
-
- int r = store->stat(coll, ghobj, &st);
- if (r < 0) {
- cerr << "remove: " << cpp_strerror(-r) << std::endl;
- return r;
- }
-
- ObjectStore::Transaction *t = new ObjectStore::Transaction;
- OSDriver::OSTransaction _t(driver.get_transaction(t));
- cout << "remove " << ghobj << std::endl;
- r = mapper.remove_oid(ghobj.hobj, &_t);
- if (r != 0 && r != -ENOENT) {
- cerr << "remove_oid returned " << cpp_strerror(-r) << std::endl;
- return r;
- }
-
- t->remove(coll, ghobj);
-
- store->apply_transaction(*t);
- delete t;
- return 0;
-}
-
-int do_list_attrs(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
-{
- map<string,bufferptr> aset;
- int r = store->getattrs(coll, ghobj, aset);
- if (r < 0) {
- cerr << "getattrs: " << cpp_strerror(-r) << std::endl;
- return r;
- }
-
- for (map<string,bufferptr>::iterator i = aset.begin();i != aset.end(); ++i) {
- string key(i->first);
- if (outistty)
- cleanbin(key);
- cout << key << std::endl;
- }
- return 0;
-}
-
-int do_list_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
-{
- ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(coll, ghobj);
- if (!iter) {
- cerr << "omap_get_iterator: " << cpp_strerror(ENOENT) << std::endl;
- return -ENOENT;
- }
- iter->seek_to_first();
- map<string, bufferlist> oset;
- while(iter->valid()) {
- get_omap_batch(iter, oset);
-
- for (map<string,bufferlist>::iterator i = oset.begin();i != oset.end(); ++i) {
- string key(i->first);
- if (outistty)
- cleanbin(key);
- cout << key << std::endl;
- }
- }
- return 0;
-}
-
-int do_get_bytes(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
-{
- struct stat st;
- mysize_t total;
-
- int ret = store->stat(coll, ghobj, &st);
- if (ret < 0) {
- cerr << "get-bytes: " << cpp_strerror(-ret) << std::endl;
- return 1;
- }
-
- total = st.st_size;
- if (debug)
- cerr << "size=" << total << std::endl;
-
- uint64_t offset = 0;
- bufferlist rawdatabl;
- while(total > 0) {
- rawdatabl.clear();
- mysize_t len = max_read;
- if (len > total)
- len = total;
-
- ret = store->read(coll, ghobj, offset, len, rawdatabl);
- if (ret < 0)
- return ret;
- if (ret == 0)
- return -EINVAL;
-
- if (debug)
- cerr << "data section offset=" << offset << " len=" << len << std::endl;
-
- total -= ret;
- offset += ret;
-
- ret = write(fd, rawdatabl.c_str(), ret);
- if (ret == -1) {
- perror("write");
- return 1;
- }
- }
-
- return 0;
-}
-
-int do_set_bytes(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
-{
- ObjectStore::Transaction tran;
- ObjectStore::Transaction *t = &tran;
-
- if (debug)
- cerr << "Write " << ghobj << std::endl;
-
- t->touch(coll, ghobj);
- t->truncate(coll, ghobj, 0);
-
- uint64_t offset = 0;
- bufferlist rawdatabl;
- do {
- rawdatabl.clear();
- ssize_t bytes = rawdatabl.read_fd(fd, max_read);
- if (bytes < 0) {
- cerr << "read_fd error " << cpp_strerror(-bytes) << std::endl;
- return 1;
- }
-
- if (bytes == 0)
- break;
-
- if (debug)
- cerr << "\tdata: offset " << offset << " bytes " << bytes << std::endl;
- t->write(coll, ghobj, offset, bytes, rawdatabl);
-
- offset += bytes;
- // XXX: Should we apply_transaction() every once in a while for very large files
- } while(true);
-
- store->apply_transaction(*t);
- return 0;
-}
-
-int do_get_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
-{
- bufferptr bp;
-
- int r = store->getattr(coll, ghobj, key.c_str(), bp);
- if (r < 0) {
- cerr << "getattr: " << cpp_strerror(-r) << std::endl;
- return r;
- }
-
- string value(bp.c_str(), bp.length());
- if (outistty) {
- cleanbin(value);
- value.push_back('\n');
- }
- cout << value;
-
- return 0;
-}
-
-int do_set_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key, int fd)
-{
- ObjectStore::Transaction tran;
- ObjectStore::Transaction *t = &tran;
- bufferlist bl;
-
- if (debug)
- cerr << "Setattr " << ghobj << std::endl;
-
- if (get_fd_data(fd, bl))
- return 1;
-
- t->touch(coll, ghobj);
-
- t->setattr(coll, ghobj, key, bl);
-
- store->apply_transaction(*t);
- return 0;
-}
-
-int do_rm_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
-{
- ObjectStore::Transaction tran;
- ObjectStore::Transaction *t = &tran;
-
- if (debug)
- cerr << "Rmattr " << ghobj << std::endl;
-
- t->rmattr(coll, ghobj, key);
-
- store->apply_transaction(*t);
- return 0;
-}
-
-int do_get_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
-{
- set<string> keys;
- map<string, bufferlist> out;
-
- keys.insert(key);
-
- int r = store->omap_get_values(coll, ghobj, keys, &out);
- if (r < 0) {
- cerr << "omap_get_values: " << cpp_strerror(-r) << std::endl;
- return r;
- }
-
- if (out.empty()) {
- cerr << "Key not found" << std::endl;
- return -ENOENT;
- }
-
- assert(out.size() == 1);
-
- bufferlist bl = out.begin()->second;
- string value(bl.c_str(), bl.length());
- if (outistty) {
- cleanbin(value);
- value.push_back('\n');
- }
- cout << value;
-
- return 0;
-}
-
-int do_set_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key, int fd)
-{
- ObjectStore::Transaction tran;
- ObjectStore::Transaction *t = &tran;
- map<string, bufferlist> attrset;
- bufferlist valbl;
-
- if (debug)
- cerr << "Set_omap " << ghobj << std::endl;
-
- if (get_fd_data(fd, valbl))
- return 1;
-
- attrset.insert(pair<string, bufferlist>(key, valbl));
-
- t->touch(coll, ghobj);
-
- t->omap_setkeys(coll, ghobj, attrset);
-
- store->apply_transaction(*t);
- return 0;
-}
-
-int do_rm_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
-{
- ObjectStore::Transaction tran;
- ObjectStore::Transaction *t = &tran;
- set<string> keys;
-
- keys.insert(key);
-
- if (debug)
- cerr << "Rm_omap " << ghobj << std::endl;
-
- t->omap_rmkeys(coll, ghobj, keys);
-
- store->apply_transaction(*t);
- return 0;
-}
-
-int do_get_omaphdr(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
-{
- bufferlist hdrbl;
-
- int r = store->omap_get_header(coll, ghobj, &hdrbl, true);
- if (r < 0) {
- cerr << "omap_get_header: " << cpp_strerror(-r) << std::endl;
- return r;
- }
-
- string header(hdrbl.c_str(), hdrbl.length());
- if (outistty) {
- cleanbin(header);
- header.push_back('\n');
- }
- cout << header;
-
- return 0;
-}
-
-int do_set_omaphdr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
-{
- ObjectStore::Transaction tran;
- ObjectStore::Transaction *t = &tran;
- bufferlist hdrbl;
-
- if (debug)
- cerr << "Omap_setheader " << ghobj << std::endl;
-
- if (get_fd_data(fd, hdrbl))
- return 1;
-
- t->touch(coll, ghobj);
-
- t->omap_setheader(coll, ghobj, hdrbl);
-
- store->apply_transaction(*t);
- return 0;
-}
-
-void usage(po::options_description &desc)
-{
- cerr << std::endl;
- cerr << desc << std::endl;
- cerr << std::endl;
- cerr << "Positional syntax:" << std::endl;
- cerr << std::endl;
- cerr << "(requires --filestore-path, --journal-path and --pgid to be specified)" << std::endl;
- cerr << "(optional [file] argument will read stdin or write stdout if not specified or if '-' specified)" << std::endl;
- cerr << "ceph-filestore-dump ... <object> (get|set)-bytes [file]" << std::endl;
- cerr << "ceph-filestore-dump ... <object> (set-(attr|omap) <key> [file]" << std::endl;
- cerr << "ceph-filestore-dump ... <object> (set-omaphdr) [file]" << std::endl;
- cerr << "ceph-filestore-dump ... <object> (get|rm)-(attr|omap) <key>" << std::endl;
- cerr << "ceph-filestore-dump ... <object> (get-omaphdr)" << std::endl;
- cerr << "ceph-filestore-dump ... <object> list-attrs" << std::endl;
- cerr << "ceph-filestore-dump ... <object> list-omap" << std::endl;
- cerr << "ceph-filestore-dump ... <object> remove" << std::endl;
- cerr << std::endl;
- exit(1);
-}
-
-int main(int argc, char **argv)
-{
- string fspath, jpath, pgidstr, type, file, object, objcmd, arg1, arg2;
- ghobject_t ghobj;
-
- po::options_description desc("Allowed options");
- desc.add_options()
- ("help", "produce help message")
- ("filestore-path", po::value<string>(&fspath),
- "path to filestore directory, mandatory")
- ("journal-path", po::value<string>(&jpath),
- "path to journal, mandatory")
- ("pgid", po::value<string>(&pgidstr),
- "PG id, mandatory except for import")
- ("type", po::value<string>(&type),
- "Arg is one of [info, log, remove, export, import, list]")
- ("file", po::value<string>(&file),
- "path of file to export or import")
- ("debug", "Enable diagnostic output to stderr")
- ("skip-journal-replay", "Disable journal replay")
- ("skip-mount-omap", "Disable mounting of omap")
- ;
-
- po::options_description positional("Positional options");
- positional.add_options()
- ("object", po::value<string>(&object), "ghobject in json")
- ("objcmd", po::value<string>(&objcmd), "command [(get|set)-bytes, (get|set|rm)-(attr|omap), list-attrs, list-omap, remove]")
- ("arg1", po::value<string>(&arg1), "arg1 based on cmd")
- ("arg2", po::value<string>(&arg2), "arg2 based on cmd")
- ;
-
- po::options_description all("All options");
- all.add(desc).add(positional);
-
- po::positional_options_description pd;
- pd.add("object", 1).add("objcmd", 1).add("arg1", 1).add("arg2", 1);
-
- po::variables_map vm;
- po::parsed_options parsed =
- po::command_line_parser(argc, argv).options(all).allow_unregistered().positional(pd).run();
- po::store( parsed, vm);
- try {
- po::notify(vm);
- }
- catch(...) {
- usage(desc);
- }
-
- if (vm.count("help")) {
- usage(desc);
- }
-
- if (!vm.count("filestore-path")) {
- cerr << "Must provide --filestore-path" << std::endl;
- usage(desc);
- }
- if (!vm.count("journal-path")) {
- cerr << "Must provide --journal-path" << std::endl;
- usage(desc);
- }
- if (vm.count("object") && !vm.count("objcmd")) {
- cerr << "Invalid syntax, missing command" << std::endl;
- usage(desc);
- }
- if (!vm.count("type") && !(vm.count("object") && vm.count("objcmd"))) {
- cerr << "Must provide --type or object command..."
- << std::endl;
- usage(desc);
- }
- if (vm.count("type") && vm.count("object")) {
- cerr << "Can't specify both --type and object command syntax" << std::endl;
- usage(desc);
- }
- if (type != "import" && !vm.count("pgid")) {
- cerr << "Must provide pgid" << std::endl;
- usage(desc);
- }
-
- if (vm.count("object")) {
- json_spirit::Value v;
- try {
- if (!json_spirit::read(object, v))
- throw std::runtime_error("bad json");
- ghobj.decode(v);
- } catch (std::runtime_error& e) {
- cerr << "error parsing offset: " << e.what() << std::endl;
- exit(1);
- }
- }
-
- outistty = isatty(STDOUT_FILENO);
-
- file_fd = fd_none;
- if (type == "export") {
- if (!vm.count("file")) {
- if (outistty) {
- cerr << "stdout is a tty and no --file option specified" << std::endl;
- exit(1);
- }
- file_fd = STDOUT_FILENO;
- } else {
- file_fd = open(file.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
- }
- } else if (type == "import") {
- if (!vm.count("file")) {
- if (isatty(STDIN_FILENO)) {
- cerr << "stdin is a tty and no --file option specified" << std::endl;
- exit(1);
- }
- file_fd = STDIN_FILENO;
- } else {
- file_fd = open(file.c_str(), O_RDONLY);
- }
- }
-
- if (vm.count("file") && file_fd == fd_none) {
- cerr << "--file option only applies to import or export" << std::endl;
- return 1;
- }
-
- if (file_fd != fd_none && file_fd < 0) {
- perror("open");
- return 1;
- }
-
- if ((fspath.length() == 0 || jpath.length() == 0) ||
- (type != "import" && pgidstr.length() == 0)) {
- cerr << "Invalid params" << std::endl;
- return 1;
- }
-
- if (type == "import" && pgidstr.length()) {
- cerr << "--pgid option invalid with import" << std::endl;
- return 1;
- }
-
- vector<const char *> ceph_options, def_args;
- vector<string> ceph_option_strings = po::collect_unrecognized(
- parsed.options, po::include_positional);
- ceph_options.reserve(ceph_option_strings.size());
- for (vector<string>::iterator i = ceph_option_strings.begin();
- i != ceph_option_strings.end();
- ++i) {
- ceph_options.push_back(i->c_str());
- }
-
- if (!vm.count("debug")) {
- debug = false;
- } else {
- debug = true;
- }
-
- osflagbits_t flags = 0;
- if (vm.count("skip-journal-replay"))
- flags |= SKIP_JOURNAL_REPLAY;
- if (vm.count("skip-mount-omap"))
- flags |= SKIP_MOUNT_OMAP;
-
- global_init(
- &def_args, ceph_options, CEPH_ENTITY_TYPE_OSD,
- CODE_ENVIRONMENT_UTILITY_NODOUT, 0);
- //CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
- common_init_finish(g_ceph_context);
- g_conf = g_ceph_context->_conf;
- if (debug) {
- g_conf->set_val_or_die("log_to_stderr", "true");
- g_conf->set_val_or_die("err_to_stderr", "true");
- }
- g_conf->apply_changes(NULL);
-
- //Verify that fspath really is an osd store
- struct stat st;
- if (::stat(fspath.c_str(), &st) == -1) {
- perror("fspath");
- invalid_path(fspath);
- }
- if (!S_ISDIR(st.st_mode)) {
- invalid_path(fspath);
- }
- string check = fspath + "/whoami";
- if (::stat(check.c_str(), &st) == -1) {
- perror("whoami");
- invalid_path(fspath);
- }
- if (!S_ISREG(st.st_mode)) {
- invalid_path(fspath);
- }
- check = fspath + "/current";
- if (::stat(check.c_str(), &st) == -1) {
- perror("current");
- invalid_path(fspath);
- }
- if (!S_ISDIR(st.st_mode)) {
- invalid_path(fspath);
- }
-
- spg_t pgid;
- if (pgidstr.length() && !pgid.parse(pgidstr.c_str())) {
- cerr << "Invalid pgid '" << pgidstr << "' specified" << std::endl;
- return 1;
- }
-
- ObjectStore *fs = ObjectStore::create(NULL, "filestore", fspath, jpath, flags);
-
- int r = fs->mount();
- if (r < 0) {
- if (r == -EBUSY) {
- cerr << "OSD has the store locked" << std::endl;
- } else {
- cerr << "Mount failed with '" << cpp_strerror(-r) << "'" << std::endl;
- }
- return 1;
- }
-
- bool fs_sharded_objects = fs->get_allow_sharded_objects();
-
- int ret = 0;
- vector<coll_t> ls;
- vector<coll_t>::iterator it;
- CompatSet supported;
-
-#ifdef INTERNAL_TEST
- supported = get_test_compat_set();
-#else
- supported = OSD::get_osd_compat_set();
-#endif
-
- bufferlist bl;
- OSDSuperblock superblock;
- bufferlist::iterator p;
- r = fs->read(META_COLL, OSD_SUPERBLOCK_POBJECT, 0, 0, bl);
- if (r < 0) {
- cerr << "Failure to read OSD superblock error= " << r << std::endl;
- goto out;
- }
-
- p = bl.begin();
- ::decode(superblock, p);
-
-#ifdef INTERNAL_TEST2
- fs->set_allow_sharded_objects();
- assert(fs->get_allow_sharded_objects());
- fs_sharded_objects = true;
- superblock.compat_features.incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SHARDS);
-#endif
-
- if (debug) {
- cerr << "Supported features: " << supported << std::endl;
- cerr << "On-disk features: " << superblock.compat_features << std::endl;
- }
- if (supported.compare(superblock.compat_features) == -1) {
- cerr << "On-disk OSD incompatible features set "
- << superblock.compat_features << std::endl;
- ret = EINVAL;
- goto out;
- }
-
- // If there was a crash as an OSD was transitioning to sharded objects
- // and hadn't completed a set_allow_sharded_objects().
- // This utility does not want to attempt to finish that transition.
- if (superblock.compat_features.incompat.contains(CEPH_OSD_FEATURE_INCOMPAT_SHARDS) != fs_sharded_objects) {
- // An OSD should never have call set_allow_sharded_objects() before
- // updating its own OSD features.
- if (fs_sharded_objects)
- cerr << "FileStore sharded but OSD not set, Corruption?" << std::endl;
- else
- cerr << "Found incomplete transition to sharded objects" << std::endl;
- ret = EINVAL;
- goto out;
- }
-
- if (type == "import") {
-
- try {
- ret = do_import(fs, superblock);
- }
- catch (const buffer::error &e) {
- cerr << "do_import threw exception error " << e.what() << std::endl;
- ret = EFAULT;
- }
- if (ret == EFAULT) {
- cerr << "Corrupt input for import" << std::endl;
- }
- if (ret == 0)
- cout << "Import successful" << std::endl;
- goto out;
- }
-
- log_oid = OSD::make_pg_log_oid(pgid);
- biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
-
- if (type == "remove") {
- uint64_t next_removal_seq = 0; //My local seq
- finish_remove_pgs(fs, &next_removal_seq);
- int r = initiate_new_remove_pg(fs, pgid, &next_removal_seq);
- if (r) {
- cerr << "PG '" << pgid << "' not found" << std::endl;
- ret = 1;
- goto out;
- }
- finish_remove_pgs(fs, &next_removal_seq);
- cout << "Remove successful" << std::endl;
- goto out;
- }
-
- r = fs->list_collections(ls);
- if (r < 0) {
- cerr << "failed to list pgs: " << cpp_strerror(-r) << std::endl;
- ret = 1;
- goto out;
- }
-
- for (it = ls.begin(); it != ls.end(); ++it) {
- snapid_t snap;
- spg_t tmppgid;
-
- if (!it->is_pg(tmppgid, snap)) {
- continue;
- }
-
- if (it->is_temp(tmppgid)) {
- continue;
- }
-
- if (tmppgid != pgid) {
- continue;
- }
- if (snap != CEPH_NOSNAP && debug) {
- cerr << "skipping snapped dir " << *it
- << " (pg " << pgid << " snap " << snap << ")" << std::endl;
- continue;
- }
-
- //Found!
- break;
- }
-
- epoch_t map_epoch;
-// The following code for export, info, log require omap or !skip-mount-omap
- if (it != ls.end()) {
-
- coll_t coll = *it;
-
- if (vm.count("objcmd")) {
- ret = 0;
- if (objcmd == "remove") {
- int r = do_remove_object(fs, coll, ghobj);
- if (r) {
- ret = 1;
- }
- goto out;
- } else if (objcmd == "list-attrs") {
- int r = do_list_attrs(fs, coll, ghobj);
- if (r) {
- ret = 1;
- }
- goto out;
- } else if (objcmd == "list-omap") {
- int r = do_list_omap(fs, coll, ghobj);
- if (r) {
- ret = 1;
- }
- goto out;
- } else if (objcmd == "get-bytes" || objcmd == "set-bytes") {
- int r;
- if (objcmd == "get-bytes") {
- int fd;
- if (vm.count("arg1") == 0 || arg1 == "-") {
- fd = STDOUT_FILENO;
- } else {
- fd = open(arg1.c_str(), O_WRONLY|O_TRUNC|O_CREAT|O_EXCL|O_LARGEFILE, 0666);
- if (fd == -1) {
- cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
- ret = 1;
- goto out;
- }
- }
- r = do_get_bytes(fs, coll, ghobj, fd);
- if (fd != STDOUT_FILENO)
- close(fd);
- } else {
- int fd;
- if (vm.count("arg1") == 0 || arg1 == "-") {
- fd = STDIN_FILENO;
- } else {
- fd = open(arg1.c_str(), O_RDONLY|O_LARGEFILE, 0666);
- if (fd == -1) {
- cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
- ret = 1;
- goto out;
- }
- }
- r = do_set_bytes(fs, coll, ghobj, fd);
- if (fd != STDIN_FILENO)
- close(fd);
- }
- if (r)
- ret = 1;
- goto out;
- } else if (objcmd == "get-attr") {
- if (vm.count("arg1") == 0)
- usage(desc);
- r = do_get_attr(fs, coll, ghobj, arg1);
- if (r)
- ret = 1;
- goto out;
- } else if (objcmd == "set-attr") {
- if (vm.count("arg1") == 0)
- usage(desc);
-
- int fd;
- if (vm.count("arg2") == 0 || arg2 == "-") {
- fd = STDIN_FILENO;
- } else {
- fd = open(arg2.c_str(), O_RDONLY|O_LARGEFILE, 0666);
- if (fd == -1) {
- cerr << "open " << arg2 << " " << cpp_strerror(errno) << std::endl;
- ret = 1;
- goto out;
- }
- }
- r = do_set_attr(fs, coll, ghobj, arg1, fd);
- if (fd != STDIN_FILENO)
- close(fd);
- if (r)
- ret = 1;
- goto out;
- } else if (objcmd == "rm-attr") {
- if (vm.count("arg1") == 0)
- usage(desc);
- r = do_rm_attr(fs, coll, ghobj, arg1);
- if (r)
- ret = 1;
- goto out;
- } else if (objcmd == "get-omap") {
- if (vm.count("arg1") == 0)
- usage(desc);
- r = do_get_omap(fs, coll, ghobj, arg1);
- if (r)
- ret = 1;
- goto out;
- } else if (objcmd == "set-omap") {
- if (vm.count("arg1") == 0)
- usage(desc);
-
- int fd;
- if (vm.count("arg2") == 0 || arg2 == "-") {
- fd = STDIN_FILENO;
- } else {
- fd = open(arg2.c_str(), O_RDONLY|O_LARGEFILE, 0666);
- if (fd == -1) {
- cerr << "open " << arg2 << " " << cpp_strerror(errno) << std::endl;
- ret = 1;
- goto out;
- }
- }
- r = do_set_omap(fs, coll, ghobj, arg1, fd);
- if (fd != STDIN_FILENO)
- close(fd);
- if (r)
- ret = 1;
- goto out;
- } else if (objcmd == "rm-omap") {
- if (vm.count("arg1") == 0)
- usage(desc);
- r = do_rm_omap(fs, coll, ghobj, arg1);
- if (r)
- ret = 1;
- goto out;
- } else if (objcmd == "get-omaphdr") {
- if (vm.count("arg1"))
- usage(desc);
- r = do_get_omaphdr(fs, coll, ghobj);
- if (r)
- ret = 1;
- goto out;
- } else if (objcmd == "set-omaphdr") {
- // Extra arg
- if (vm.count("arg2"))
- usage(desc);
- int fd;
- if (vm.count("arg1") == 0 || arg1 == "-") {
- fd = STDIN_FILENO;
- } else {
- fd = open(arg1.c_str(), O_RDONLY|O_LARGEFILE, 0666);
- if (fd == -1) {
- cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
- ret = 1;
- goto out;
- }
- }
- r = do_set_omaphdr(fs, coll, ghobj, fd);
- if (fd != STDIN_FILENO)
- close(fd);
- if (r)
- ret = 1;
- goto out;
- }
- cerr << "Unknown object command '" << objcmd << "'" << std::endl;
- usage(desc);
- }
-
- if (type == "list") {
- Formatter *formatter = new JSONFormatter(false);
- r = do_list(fs, coll, formatter);
- if (r) {
- cerr << "do_list failed with " << r << std::endl;
- ret = 1;
- }
- goto out;
- }
-
- Formatter *formatter = new JSONFormatter(true);
- bufferlist bl;
- map_epoch = PG::peek_map_epoch(fs, coll, infos_oid, &bl);
- if (debug)
- cerr << "map_epoch " << map_epoch << std::endl;
-
- pg_info_t info(pgid);
- map<epoch_t,pg_interval_t> past_intervals;
- hobject_t biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
- interval_set<snapid_t> snap_collections;
-
- __u8 struct_ver;
- r = PG::read_info(fs, coll, bl, info, past_intervals, biginfo_oid,
- infos_oid, snap_collections, struct_ver);
- if (r < 0) {
- cerr << "read_info error " << cpp_strerror(-r) << std::endl;
- ret = 1;
- goto out;
- }
- if (debug)
- cerr << "struct_v " << (int)struct_ver << std::endl;
-
- if (type == "export") {
- ret = do_export(fs, coll, pgid, info, map_epoch, struct_ver, superblock);
- if (ret == 0 && file_fd != STDOUT_FILENO)
- cout << "Export successful" << std::endl;
- } else if (type == "info") {
- formatter->open_object_section("info");
- info.dump(formatter);
- formatter->close_section();
- formatter->flush(cout);
- cout << std::endl;
- } else if (type == "log") {
- PGLog::IndexedLog log;
- pg_missing_t missing;
- ret = get_log(fs, coll, pgid, info, log, missing);
- if (ret > 0)
- goto out;
-
- formatter->open_object_section("log");
- log.dump(formatter);
- formatter->close_section();
- formatter->flush(cout);
- cout << std::endl;
- formatter->open_object_section("missing");
- missing.dump(formatter);
- formatter->close_section();
- formatter->flush(cout);
- cout << std::endl;
- } else {
- cerr << "Must provide --type (info, log, remove, export, import, list)"
- << std::endl;
- usage(desc);
- }
- } else {
- cerr << "PG '" << pgid << "' not found" << std::endl;
- ret = 1;
- }
-
-out:
- if (fs->umount() < 0) {
- cerr << "umount failed" << std::endl;
- return 1;
- }
-
- return (ret != 0);
-}
-
+++ /dev/null
-// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
-// vim: ts=8 sw=2 smarttab
-/*
- * Ceph - scalable distributed file system
- *
- * Copyright (C) 2013 Inktank
- *
- * This is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License version 2.1, as published by the Free Software
- * Foundation. See file COPYING.
- *
- */
-
-#include <boost/program_options/variables_map.hpp>
-#include <boost/program_options/parsers.hpp>
-
-#include <stdlib.h>
-
-#include "common/Formatter.h"
-#include "common/errno.h"
-
-#include "global/global_init.h"
-
-#include "os/ObjectStore.h"
-#include "os/FileStore.h"
-
-#include "osd/PGLog.h"
-#include "osd/osd_types.h"
-#include "osd/OSD.h"
-
-namespace po = boost::program_options;
-using namespace std;
-
-static void invalid_path(string &path)
-{
- cout << "Invalid path to osd store specified: " << path << "\n";
- exit(1);
-}
-
-int main(int argc, char **argv)
-{
- string fspath, jpath, pgidstr;
- bool list_lost_objects = false;
- bool fix_lost_objects = false;
- unsigned LIST_AT_A_TIME = 100;
- unsigned scanned = 0;
-
- po::options_description desc("Allowed options");
- desc.add_options()
- ("help", "produce help message")
- ("filestore-path", po::value<string>(&fspath),
- "path to filestore directory, mandatory")
- ("journal-path", po::value<string>(&jpath),
- "path to journal, mandatory")
- ("pgid", po::value<string>(&pgidstr),
- "PG id")
- ("list-lost-objects", po::value<bool>(
- &list_lost_objects)->default_value(false),
- "list lost objects")
- ("fix-lost-objects", po::value<bool>(
- &fix_lost_objects)->default_value(false),
- "fix lost objects")
- ;
-
- po::variables_map vm;
- po::parsed_options parsed =
- po::command_line_parser(argc, argv).options(desc).
- allow_unregistered().run();
- po::store( parsed, vm);
- try {
- po::notify(vm);
- }
- catch(...) {
- cout << desc << std::endl;
- exit(1);
- }
-
- if (vm.count("help")) {
- cout << desc << std::endl;
- return 1;
- }
-
- if (!vm.count("filestore-path")) {
- cerr << "Must provide filestore-path" << std::endl
- << desc << std::endl;
- return 1;
- }
- if (!vm.count("journal-path")) {
- cerr << "Must provide journal-path" << std::endl
- << desc << std::endl;
- return 1;
- }
-
- if ((fspath.length() == 0 || jpath.length() == 0)) {
- cerr << "Invalid params" << desc << std::endl;
- exit(1);
- }
-
- vector<const char *> ceph_options, def_args;
- vector<string> ceph_option_strings = po::collect_unrecognized(
- parsed.options, po::include_positional);
- ceph_options.reserve(ceph_option_strings.size());
- for (vector<string>::iterator i = ceph_option_strings.begin();
- i != ceph_option_strings.end();
- ++i) {
- ceph_options.push_back(i->c_str());
- }
-
- global_init(
- &def_args, ceph_options, CEPH_ENTITY_TYPE_OSD,
- CODE_ENVIRONMENT_UTILITY, 0);
- //CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
- common_init_finish(g_ceph_context);
- g_ceph_context->_conf->apply_changes(NULL);
- g_conf = g_ceph_context->_conf;
-
- //Verify that fspath really is an osd store
- struct stat st;
- if (::stat(fspath.c_str(), &st) == -1) {
- perror("fspath");
- invalid_path(fspath);
- }
- if (!S_ISDIR(st.st_mode)) {
- invalid_path(fspath);
- }
- string check = fspath + "/whoami";
- if (::stat(check.c_str(), &st) == -1) {
- perror("whoami");
- invalid_path(fspath);
- }
- if (!S_ISREG(st.st_mode)) {
- invalid_path(fspath);
- }
- check = fspath + "/current";
- if (::stat(check.c_str(), &st) == -1) {
- perror("current");
- invalid_path(fspath);
- }
- if (!S_ISDIR(st.st_mode)) {
- invalid_path(fspath);
- }
-
- ObjectStore *fs = new FileStore(fspath, jpath);
-
- int r = fs->mount();
- if (r < 0) {
- if (r == -EBUSY) {
- cout << "OSD has the store locked" << std::endl;
- } else {
- cout << "Mount failed with '" << cpp_strerror(-r) << "'" << std::endl;
- }
- return 1;
- }
-
- vector<coll_t> colls_to_check;
- if (pgidstr.length()) {
- spg_t pgid;
- if (!pgid.parse(pgidstr.c_str())) {
- cout << "Invalid pgid '" << pgidstr << "' specified" << std::endl;
- exit(1);
- }
- colls_to_check.push_back(coll_t(pgid));
- } else {
- vector<coll_t> candidates;
- r = fs->list_collections(candidates);
- if (r < 0) {
- cerr << "Error listing collections: " << cpp_strerror(r) << std::endl;
- goto UMOUNT;
- }
- for (vector<coll_t>::iterator i = candidates.begin();
- i != candidates.end();
- ++i) {
- spg_t pgid;
- snapid_t snap;
- if (i->is_pg(pgid, snap)) {
- colls_to_check.push_back(*i);
- }
- }
- }
-
- cerr << colls_to_check.size() << " pgs to scan" << std::endl;
- for (vector<coll_t>::iterator i = colls_to_check.begin();
- i != colls_to_check.end();
- ++i, ++scanned) {
- cerr << "Scanning " << *i << ", " << scanned << "/"
- << colls_to_check.size() << " completed" << std::endl;
- ghobject_t next;
- while (!next.is_max()) {
- vector<ghobject_t> list;
- r = fs->collection_list_partial(
- *i,
- next,
- LIST_AT_A_TIME,
- LIST_AT_A_TIME,
- CEPH_NOSNAP,
- &list,
- &next);
- if (r < 0) {
- cerr << "Error listing collection: " << *i << ", "
- << cpp_strerror(r) << std::endl;
- goto UMOUNT;
- }
- for (vector<ghobject_t>::iterator obj = list.begin();
- obj != list.end();
- ++obj) {
- bufferlist attr;
- r = fs->getattr(*i, *obj, OI_ATTR, attr);
- if (r < 0) {
- cerr << "Error getting attr on : " << make_pair(*i, *obj) << ", "
- << cpp_strerror(r) << std::endl;
- goto UMOUNT;
- }
- object_info_t oi;
- bufferlist::iterator bp = attr.begin();
- try {
- ::decode(oi, bp);
- } catch (...) {
- r = -EINVAL;
- cerr << "Error getting attr on : " << make_pair(*i, *obj) << ", "
- << cpp_strerror(r) << std::endl;
- goto UMOUNT;
- }
- if (oi.is_lost()) {
- if (list_lost_objects) {
- cout << *i << "/" << *obj << " is lost" << std::endl;
- }
- if (fix_lost_objects) {
- cerr << *i << "/" << *obj << " is lost, fixing" << std::endl;
- oi.clear_flag(object_info_t::FLAG_LOST);
- bufferlist bl2;
- ::encode(oi, bl2);
- ObjectStore::Transaction t;
- t.setattr(*i, *obj, OI_ATTR, bl2);
- r = fs->apply_transaction(t);
- if (r < 0) {
- cerr << "Error getting fixing attr on : " << make_pair(*i, *obj)
- << ", "
- << cpp_strerror(r) << std::endl;
- goto UMOUNT;
- }
- }
- }
- }
- }
- }
- cerr << "Completed" << std::endl;
-
- UMOUNT:
- fs->sync_and_flush();
- fs->umount();
- return r;
-}
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+/*
+ * Ceph - scalable distributed file system
+ *
+ * Copyright (C) 2013 Inktank
+ *
+ * This is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License version 2.1, as published by the Free Software
+ * Foundation. See file COPYING.
+ *
+ */
+
+#include <boost/program_options/variables_map.hpp>
+#include <boost/program_options/parsers.hpp>
+
+#include <stdlib.h>
+
+#include "common/Formatter.h"
+#include "common/errno.h"
+
+#include "global/global_init.h"
+
+#include "os/ObjectStore.h"
+#include "os/FileStore.h"
+
+#include "osd/PGLog.h"
+#include "osd/OSD.h"
+
+#include "json_spirit/json_spirit_value.h"
+#include "json_spirit/json_spirit_reader.h"
+
+namespace po = boost::program_options;
+using namespace std;
+
+static coll_t META_COLL("meta");
+
+enum {
+ TYPE_NONE = 0,
+ TYPE_PG_BEGIN,
+ TYPE_PG_END,
+ TYPE_OBJECT_BEGIN,
+ TYPE_OBJECT_END,
+ TYPE_DATA,
+ TYPE_ATTRS,
+ TYPE_OMAP_HDR,
+ TYPE_OMAP,
+ TYPE_PG_METADATA,
+ END_OF_TYPES, //Keep at the end
+};
+
+//#define INTERNAL_TEST
+//#define INTERNAL_TEST2
+
+#ifdef INTERNAL_TEST
+CompatSet get_test_compat_set() {
+ CompatSet::FeatureSet ceph_osd_feature_compat;
+ CompatSet::FeatureSet ceph_osd_feature_ro_compat;
+ CompatSet::FeatureSet ceph_osd_feature_incompat;
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_BASE);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_PGINFO);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_OLOC);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEC);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_CATEGORIES);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_HOBJECTPOOL);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_BIGINFO);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEVELDBINFO);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_LEVELDBLOG);
+#ifdef INTERNAL_TEST2
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SNAPMAPPER);
+ ceph_osd_feature_incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SHARDS);
+#endif
+ return CompatSet(ceph_osd_feature_compat, ceph_osd_feature_ro_compat,
+ ceph_osd_feature_incompat);
+}
+#endif
+
+typedef uint8_t sectiontype_t;
+typedef uint32_t mymagic_t;
+typedef int64_t mysize_t;
+const ssize_t max_read = 1024 * 1024;
+const uint16_t shortmagic = 0xffce; //goes into stream as "ceff"
+//endmagic goes into stream as "ceff ffec"
+const mymagic_t endmagic = (0xecff << 16) | shortmagic;
+const int fd_none = INT_MIN;
+bool outistty;
+
+//The first FIXED_LENGTH bytes are a fixed
+//portion of the export output. This includes the overall
+//version number, and size of header and footer.
+//THIS STRUCTURE CAN ONLY BE APPENDED TO. If it needs to expand,
+//the version can be bumped and then anything
+//can be added to the export format.
+struct super_header {
+ static const uint32_t super_magic = (shortmagic << 16) | shortmagic;
+ static const uint32_t super_ver = 2;
+ static const uint32_t FIXED_LENGTH = 16;
+ uint32_t magic;
+ uint32_t version;
+ uint32_t header_size;
+ uint32_t footer_size;
+
+ super_header() : magic(0), version(0), header_size(0), footer_size(0) { }
+ int read_super();
+
+ void encode(bufferlist& bl) const {
+ ::encode(magic, bl);
+ ::encode(version, bl);
+ ::encode(header_size, bl);
+ ::encode(footer_size, bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ ::decode(magic, bl);
+ ::decode(version, bl);
+ ::decode(header_size, bl);
+ ::decode(footer_size, bl);
+ }
+};
+
+struct header {
+ sectiontype_t type;
+ mysize_t size;
+ header(sectiontype_t type, mysize_t size) :
+ type(type), size(size) { }
+ header(): type(0), size(0) { }
+
+ int get_header();
+
+ void encode(bufferlist& bl) const {
+ uint32_t debug_type = (type << 24) | (type << 16) | shortmagic;
+ ENCODE_START(1, 1, bl);
+ ::encode(debug_type, bl);
+ ::encode(size, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ uint32_t debug_type;
+ DECODE_START(1, bl);
+ ::decode(debug_type, bl);
+ type = debug_type >> 24;
+ ::decode(size, bl);
+ DECODE_FINISH(bl);
+ }
+};
+
+struct footer {
+ mymagic_t magic;
+ footer() : magic(endmagic) { }
+
+ int get_footer();
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(magic, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(1, bl);
+ ::decode(magic, bl);
+ DECODE_FINISH(bl);
+ }
+};
+
+struct pg_begin {
+ spg_t pgid;
+ OSDSuperblock superblock;
+
+ pg_begin(spg_t pg, const OSDSuperblock& sb):
+ pgid(pg), superblock(sb) { }
+ pg_begin() { }
+
+ void encode(bufferlist& bl) const {
+ // If superblock doesn't include CEPH_FS_FEATURE_INCOMPAT_SHARDS then
+ // shard will be NO_SHARD for a replicated pool. This means
+ // that we allow the decode by struct_v 2.
+ ENCODE_START(3, 2, bl);
+ ::encode(pgid.pgid, bl);
+ ::encode(superblock, bl);
+ ::encode(pgid.shard, bl);
+ ENCODE_FINISH(bl);
+ }
+ // NOTE: New super_ver prevents decode from ver 1
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(3, bl);
+ ::decode(pgid.pgid, bl);
+ if (struct_v > 1) {
+ ::decode(superblock, bl);
+ }
+ if (struct_v > 2) {
+ ::decode(pgid.shard, bl);
+ } else {
+ pgid.shard = shard_id_t::NO_SHARD;
+ }
+ DECODE_FINISH(bl);
+ }
+};
+
+struct object_begin {
+ ghobject_t hoid;
+ object_begin(const ghobject_t &hoid): hoid(hoid) { }
+ object_begin() { }
+
+ // If superblock doesn't include CEPH_FS_FEATURE_INCOMPAT_SHARDS then
+ // generation will be NO_GEN, shard_id will be NO_SHARD for a replicated
+ // pool. This means we will allow the decode by struct_v 1.
+ void encode(bufferlist& bl) const {
+ ENCODE_START(2, 1, bl);
+ ::encode(hoid.hobj, bl);
+ ::encode(hoid.generation, bl);
+ ::encode(hoid.shard_id, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(2, bl);
+ ::decode(hoid.hobj, bl);
+ if (struct_v > 1) {
+ ::decode(hoid.generation, bl);
+ ::decode(hoid.shard_id, bl);
+ } else {
+ hoid.generation = ghobject_t::NO_GEN;
+ hoid.shard_id = shard_id_t::NO_SHARD;
+ }
+ DECODE_FINISH(bl);
+ }
+};
+
+struct data_section {
+ uint64_t offset;
+ uint64_t len;
+ bufferlist databl;
+ data_section(uint64_t offset, uint64_t len, bufferlist bl):
+ offset(offset), len(len), databl(bl) { }
+ data_section(): offset(0), len(0) { }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(offset, bl);
+ ::encode(len, bl);
+ ::encode(databl, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(1, bl);
+ ::decode(offset, bl);
+ ::decode(len, bl);
+ ::decode(databl, bl);
+ DECODE_FINISH(bl);
+ }
+};
+
+struct attr_section {
+ map<string,bufferptr> data;
+ attr_section(const map<string,bufferptr> &data) : data(data) { }
+ attr_section() { }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(data, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(1, bl);
+ ::decode(data, bl);
+ DECODE_FINISH(bl);
+ }
+};
+
+struct omap_hdr_section {
+ bufferlist hdr;
+ omap_hdr_section(bufferlist hdr) : hdr(hdr) { }
+ omap_hdr_section() { }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(hdr, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(1, bl);
+ ::decode(hdr, bl);
+ DECODE_FINISH(bl);
+ }
+};
+
+struct omap_section {
+ map<string, bufferlist> omap;
+ omap_section(const map<string, bufferlist> &omap) :
+ omap(omap) { }
+ omap_section() { }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(omap, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(1, bl);
+ ::decode(omap, bl);
+ DECODE_FINISH(bl);
+ }
+};
+
+struct metadata_section {
+ __u8 struct_ver;
+ epoch_t map_epoch;
+ pg_info_t info;
+ pg_log_t log;
+
+ metadata_section(__u8 struct_ver, epoch_t map_epoch, const pg_info_t &info,
+ const pg_log_t &log)
+ : struct_ver(struct_ver),
+ map_epoch(map_epoch),
+ info(info),
+ log(log) { }
+ metadata_section()
+ : struct_ver(0),
+ map_epoch(0) { }
+
+ void encode(bufferlist& bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(struct_ver, bl);
+ ::encode(map_epoch, bl);
+ ::encode(info, bl);
+ ::encode(log, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator& bl) {
+ DECODE_START(1, bl);
+ ::decode(struct_ver, bl);
+ ::decode(map_epoch, bl);
+ ::decode(info, bl);
+ ::decode(log, bl);
+ DECODE_FINISH(bl);
+ }
+};
+
+hobject_t infos_oid = OSD::make_infos_oid();
+hobject_t biginfo_oid, log_oid;
+
+int file_fd = fd_none;
+bool debug = false;
+super_header sh;
+
+template <typename T>
+int write_section(sectiontype_t type, const T& obj, int fd) {
+ bufferlist blhdr, bl, blftr;
+ obj.encode(bl);
+ header hdr(type, bl.length());
+ hdr.encode(blhdr);
+ footer ft;
+ ft.encode(blftr);
+
+ int ret = blhdr.write_fd(fd);
+ if (ret) return ret;
+ ret = bl.write_fd(fd);
+ if (ret) return ret;
+ ret = blftr.write_fd(fd);
+ return ret;
+}
+
+// Convert non-printable characters to '\###'
+static void cleanbin(string &str)
+{
+ bool cleaned = false;
+ string clean;
+
+ for (string::iterator it = str.begin(); it != str.end(); ++it) {
+ if (!isprint(*it)) {
+ clean.push_back('\\');
+ clean.push_back('0' + ((*it >> 6) & 7));
+ clean.push_back('0' + ((*it >> 3) & 7));
+ clean.push_back('0' + (*it & 7));
+ cleaned = true;
+ } else {
+ clean.push_back(*it);
+ }
+ }
+
+ if (cleaned)
+ str = clean;
+ return;
+}
+
+int write_simple(sectiontype_t type, int fd)
+{
+ bufferlist hbl;
+
+ header hdr(type, 0);
+ hdr.encode(hbl);
+ return hbl.write_fd(fd);
+}
+
+static int get_fd_data(int fd, bufferlist &bl)
+{
+ uint64_t total = 0;
+ do {
+ ssize_t bytes = bl.read_fd(fd, max_read);
+ if (bytes < 0) {
+ cerr << "read_fd error " << cpp_strerror(-bytes) << std::endl;
+ return 1;
+ }
+
+ if (bytes == 0)
+ break;
+
+ total += bytes;
+ } while(true);
+
+ assert(bl.length() == total);
+ return 0;
+}
+
+static void invalid_path(string &path)
+{
+ cerr << "Invalid path to osd store specified: " << path << "\n";
+ exit(1);
+}
+
+int get_log(ObjectStore *fs, coll_t coll, spg_t pgid, const pg_info_t &info,
+ PGLog::IndexedLog &log, pg_missing_t &missing)
+{
+ map<eversion_t, hobject_t> divergent_priors;
+ try {
+ ostringstream oss;
+ PGLog::read_log(fs, coll, log_oid, info, divergent_priors, log, missing, oss);
+ if (debug && oss.str().size())
+ cerr << oss.str() << std::endl;
+ }
+ catch (const buffer::error &e) {
+ cerr << "read_log threw exception error " << e.what() << std::endl;
+ return 1;
+ }
+ return 0;
+}
+
+//Based on RemoveWQ::_process()
+void remove_coll(ObjectStore *store, const coll_t &coll)
+{
+ spg_t pg;
+ coll.is_pg_prefix(pg);
+ OSDriver driver(
+ store,
+ coll_t(),
+ OSD::make_snapmapper_oid());
+ SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
+
+ vector<ghobject_t> objects;
+ ghobject_t next;
+ int r = 0;
+ int64_t num = 0;
+ ObjectStore::Transaction *t = new ObjectStore::Transaction;
+ cout << "remove_coll " << coll << std::endl;
+ while (!next.is_max()) {
+ r = store->collection_list_partial(coll, next, 200, 300, 0,
+ &objects, &next);
+ if (r < 0)
+ goto out;
+ for (vector<ghobject_t>::iterator i = objects.begin();
+ i != objects.end();
+ ++i, ++num) {
+
+ OSDriver::OSTransaction _t(driver.get_transaction(t));
+ cout << "remove " << *i << std::endl;
+ int r = mapper.remove_oid(i->hobj, &_t);
+ if (r != 0 && r != -ENOENT) {
+ assert(0);
+ }
+
+ t->remove(coll, *i);
+ if (num >= 30) {
+ store->apply_transaction(*t);
+ delete t;
+ t = new ObjectStore::Transaction;
+ num = 0;
+ }
+ }
+ }
+ t->remove_collection(coll);
+ store->apply_transaction(*t);
+out:
+ delete t;
+}
+
+//Based on part of OSD::load_pgs()
+int finish_remove_pgs(ObjectStore *store, uint64_t *next_removal_seq)
+{
+ vector<coll_t> ls;
+ int r = store->list_collections(ls);
+ if (r < 0) {
+ cerr << "finish_remove_pgs: failed to list pgs: " << cpp_strerror(-r)
+ << std::endl;
+ return r;
+ }
+
+ for (vector<coll_t>::iterator it = ls.begin();
+ it != ls.end();
+ ++it) {
+ spg_t pgid;
+ snapid_t snap;
+
+ if (it->is_temp(pgid)) {
+ cout << "finish_remove_pgs " << *it << " clearing temp" << std::endl;
+ OSD::recursive_remove_collection(store, *it);
+ continue;
+ }
+
+ if (it->is_pg(pgid, snap)) {
+ continue;
+ }
+
+ uint64_t seq;
+ if (it->is_removal(&seq, &pgid)) {
+ if (seq >= *next_removal_seq)
+ *next_removal_seq = seq + 1;
+ cout << "finish_remove_pgs removing " << *it << ", seq is "
+ << seq << " pgid is " << pgid << std::endl;
+ remove_coll(store, *it);
+ continue;
+ }
+
+ //cout << "finish_remove_pgs ignoring unrecognized " << *it << std::endl;
+ }
+ return 0;
+}
+
+int initiate_new_remove_pg(ObjectStore *store, spg_t r_pgid,
+ uint64_t *next_removal_seq)
+{
+ ObjectStore::Transaction *rmt = new ObjectStore::Transaction;
+
+ if (store->collection_exists(coll_t(r_pgid))) {
+ coll_t to_remove = coll_t::make_removal_coll((*next_removal_seq)++, r_pgid);
+ cout << "collection rename " << coll_t(r_pgid)
+ << " to " << to_remove
+ << std::endl;
+ rmt->collection_rename(coll_t(r_pgid), to_remove);
+ } else {
+ delete rmt;
+ return ENOENT;
+ }
+
+ cout << "remove " << META_COLL << " " << log_oid.oid << std::endl;
+ rmt->remove(META_COLL, log_oid);
+ cout << "remove " << META_COLL << " " << biginfo_oid.oid << std::endl;
+ rmt->remove(META_COLL, biginfo_oid);
+
+ store->apply_transaction(*rmt);
+
+ return 0;
+}
+
+int header::get_header()
+{
+ bufferlist ebl;
+ bufferlist::iterator ebliter = ebl.begin();
+ ssize_t bytes;
+
+ bytes = ebl.read_fd(file_fd, sh.header_size);
+ if ((size_t)bytes != sh.header_size) {
+ cerr << "Unexpected EOF" << std::endl;
+ return EFAULT;
+ }
+
+ decode(ebliter);
+
+ return 0;
+}
+
+int footer::get_footer()
+{
+ bufferlist ebl;
+ bufferlist::iterator ebliter = ebl.begin();
+ ssize_t bytes;
+
+ bytes = ebl.read_fd(file_fd, sh.footer_size);
+ if ((size_t)bytes != sh.footer_size) {
+ cerr << "Unexpected EOF" << std::endl;
+ return EFAULT;
+ }
+
+ decode(ebliter);
+
+ if (magic != endmagic) {
+ cerr << "Bad footer magic" << std::endl;
+ return EFAULT;
+ }
+
+ return 0;
+}
+
+int write_info(ObjectStore::Transaction &t, epoch_t epoch, pg_info_t &info,
+ __u8 struct_ver)
+{
+ //Empty for this
+ interval_set<snapid_t> snap_collections; // obsolete
+ map<epoch_t,pg_interval_t> past_intervals;
+ coll_t coll(info.pgid);
+
+ int ret = PG::_write_info(t, epoch,
+ info, coll,
+ past_intervals,
+ snap_collections,
+ infos_oid,
+ struct_ver,
+ true, true);
+ if (ret < 0) ret = -ret;
+ if (ret) cerr << "Failed to write info" << std::endl;
+ return ret;
+}
+
+void write_log(ObjectStore::Transaction &t, pg_log_t &log)
+{
+ map<eversion_t, hobject_t> divergent_priors;
+ PGLog::write_log(t, log, log_oid, divergent_priors);
+}
+
+int write_pg(ObjectStore::Transaction &t, epoch_t epoch, pg_info_t &info,
+ pg_log_t &log, __u8 struct_ver)
+{
+ int ret = write_info(t, epoch, info, struct_ver);
+ if (ret) return ret;
+ write_log(t, log);
+ return 0;
+}
+
+const int OMAP_BATCH_SIZE = 25;
+void get_omap_batch(ObjectMap::ObjectMapIterator &iter, map<string, bufferlist> &oset)
+{
+ oset.clear();
+ for (int count = OMAP_BATCH_SIZE; count && iter->valid(); --count, iter->next()) {
+ oset.insert(pair<string, bufferlist>(iter->key(), iter->value()));
+ }
+}
+
+int export_file(ObjectStore *store, coll_t cid, ghobject_t &obj)
+{
+ struct stat st;
+ mysize_t total;
+ footer ft;
+
+ int ret = store->stat(cid, obj, &st);
+ if (ret < 0)
+ return ret;
+
+ cerr << "read " << obj << std::endl;
+
+ total = st.st_size;
+ if (debug)
+ cerr << "size=" << total << std::endl;
+
+ object_begin objb(obj);
+ ret = write_section(TYPE_OBJECT_BEGIN, objb, file_fd);
+ if (ret < 0)
+ return ret;
+
+ uint64_t offset = 0;
+ bufferlist rawdatabl;
+ while(total > 0) {
+ rawdatabl.clear();
+ mysize_t len = max_read;
+ if (len > total)
+ len = total;
+
+ ret = store->read(cid, obj, offset, len, rawdatabl);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EINVAL;
+
+ data_section dblock(offset, len, rawdatabl);
+ if (debug)
+ cerr << "data section offset=" << offset << " len=" << len << std::endl;
+
+ total -= ret;
+ offset += ret;
+
+ ret = write_section(TYPE_DATA, dblock, file_fd);
+ if (ret) return ret;
+ }
+
+ //Handle attrs for this object
+ map<string,bufferptr> aset;
+ ret = store->getattrs(cid, obj, aset);
+ if (ret) return ret;
+ attr_section as(aset);
+ ret = write_section(TYPE_ATTRS, as, file_fd);
+ if (ret)
+ return ret;
+
+ if (debug) {
+ cerr << "attrs size " << aset.size() << std::endl;
+ }
+
+ //Handle omap information
+ bufferlist hdrbuf;
+ ret = store->omap_get_header(cid, obj, &hdrbuf, true);
+ if (ret < 0) {
+ cerr << "omap_get_header: " << cpp_strerror(-ret) << std::endl;
+ return ret;
+ }
+
+ omap_hdr_section ohs(hdrbuf);
+ ret = write_section(TYPE_OMAP_HDR, ohs, file_fd);
+ if (ret)
+ return ret;
+
+ ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(cid, obj);
+ if (!iter) {
+ ret = -ENOENT;
+ cerr << "omap_get_iterator: " << cpp_strerror(-ret) << std::endl;
+ return ret;
+ }
+ iter->seek_to_first();
+ int mapcount = 0;
+ map<string, bufferlist> out;
+ while(iter->valid()) {
+ get_omap_batch(iter, out);
+
+ if (out.empty()) break;
+
+ mapcount += out.size();
+ omap_section oms(out);
+ ret = write_section(TYPE_OMAP, oms, file_fd);
+ if (ret)
+ return ret;
+ }
+ if (debug)
+ cerr << "omap map size " << mapcount << std::endl;
+
+ ret = write_simple(TYPE_OBJECT_END, file_fd);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int export_files(ObjectStore *store, coll_t coll)
+{
+ vector<ghobject_t> objects;
+ ghobject_t next;
+
+ while (!next.is_max()) {
+ int r = store->collection_list_partial(coll, next, 200, 300, 0,
+ &objects, &next);
+ if (r < 0)
+ return r;
+ for (vector<ghobject_t>::iterator i = objects.begin();
+ i != objects.end();
+ ++i) {
+ r = export_file(store, coll, *i);
+ if (r < 0)
+ return r;
+ }
+ }
+ return 0;
+}
+
+//Write super_header with its fixed 16 byte length
+void write_super()
+{
+ bufferlist superbl;
+ super_header sh;
+ footer ft;
+
+ header hdr(TYPE_NONE, 0);
+ hdr.encode(superbl);
+
+ sh.magic = super_header::super_magic;
+ sh.version = super_header::super_ver;
+ sh.header_size = superbl.length();
+ superbl.clear();
+ ft.encode(superbl);
+ sh.footer_size = superbl.length();
+ superbl.clear();
+
+ sh.encode(superbl);
+ assert(super_header::FIXED_LENGTH == superbl.length());
+ superbl.write_fd(file_fd);
+}
+
+int do_export(ObjectStore *fs, coll_t coll, spg_t pgid, pg_info_t &info,
+ epoch_t map_epoch, __u8 struct_ver, const OSDSuperblock& superblock)
+{
+ PGLog::IndexedLog log;
+ pg_missing_t missing;
+
+ cerr << "Exporting " << pgid << std::endl;
+
+ int ret = get_log(fs, coll, pgid, info, log, missing);
+ if (ret > 0)
+ return ret;
+
+ write_super();
+
+ pg_begin pgb(pgid, superblock);
+ ret = write_section(TYPE_PG_BEGIN, pgb, file_fd);
+ if (ret)
+ return ret;
+
+ ret = export_files(fs, coll);
+ if (ret) {
+ cerr << "export_files error " << ret << std::endl;
+ return ret;
+ }
+
+ metadata_section ms(struct_ver, map_epoch, info, log);
+ ret = write_section(TYPE_PG_METADATA, ms, file_fd);
+ if (ret)
+ return ret;
+
+ ret = write_simple(TYPE_PG_END, file_fd);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+int super_header::read_super()
+{
+ bufferlist ebl;
+ bufferlist::iterator ebliter = ebl.begin();
+ ssize_t bytes;
+
+ bytes = ebl.read_fd(file_fd, super_header::FIXED_LENGTH);
+ if ((size_t)bytes != super_header::FIXED_LENGTH) {
+ cerr << "Unexpected EOF" << std::endl;
+ return EFAULT;
+ }
+
+ decode(ebliter);
+
+ return 0;
+}
+
+int read_section(int fd, sectiontype_t *type, bufferlist *bl)
+{
+ header hdr;
+ ssize_t bytes;
+
+ int ret = hdr.get_header();
+ if (ret)
+ return ret;
+
+ *type = hdr.type;
+
+ bl->clear();
+ bytes = bl->read_fd(fd, hdr.size);
+ if (bytes != hdr.size) {
+ cerr << "Unexpected EOF" << std::endl;
+ return EFAULT;
+ }
+
+ if (hdr.size > 0) {
+ footer ft;
+ ret = ft.get_footer();
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int get_data(ObjectStore *store, coll_t coll, ghobject_t hoid,
+ ObjectStore::Transaction *t, bufferlist &bl)
+{
+ bufferlist::iterator ebliter = bl.begin();
+ data_section ds;
+ ds.decode(ebliter);
+
+ if (debug)
+ cerr << "\tdata: offset " << ds.offset << " len " << ds.len << std::endl;
+ t->write(coll, hoid, ds.offset, ds.len, ds.databl);
+ return 0;
+}
+
+int get_attrs(ObjectStore *store, coll_t coll, ghobject_t hoid,
+ ObjectStore::Transaction *t, bufferlist &bl,
+ OSDriver &driver, SnapMapper &snap_mapper)
+{
+ bufferlist::iterator ebliter = bl.begin();
+ attr_section as;
+ as.decode(ebliter);
+
+ if (debug)
+ cerr << "\tattrs: len " << as.data.size() << std::endl;
+ t->setattrs(coll, hoid, as.data);
+
+ if (hoid.hobj.snap < CEPH_MAXSNAP && hoid.generation == ghobject_t::NO_GEN) {
+ map<string,bufferptr>::iterator mi = as.data.find(OI_ATTR);
+ if (mi != as.data.end()) {
+ bufferlist attr_bl;
+ attr_bl.push_back(mi->second);
+ object_info_t oi(attr_bl);
+
+ if (debug)
+ cerr << "object_info " << oi << std::endl;
+
+ OSDriver::OSTransaction _t(driver.get_transaction(t));
+ set<snapid_t> oi_snaps(oi.snaps.begin(), oi.snaps.end());
+ snap_mapper.add_oid(hoid.hobj, oi_snaps, &_t);
+ }
+ }
+
+ return 0;
+}
+
+int get_omap_hdr(ObjectStore *store, coll_t coll, ghobject_t hoid,
+ ObjectStore::Transaction *t, bufferlist &bl)
+{
+ bufferlist::iterator ebliter = bl.begin();
+ omap_hdr_section oh;
+ oh.decode(ebliter);
+
+ if (debug)
+ cerr << "\tomap header: " << string(oh.hdr.c_str(), oh.hdr.length())
+ << std::endl;
+ t->omap_setheader(coll, hoid, oh.hdr);
+ return 0;
+}
+
+int get_omap(ObjectStore *store, coll_t coll, ghobject_t hoid,
+ ObjectStore::Transaction *t, bufferlist &bl)
+{
+ bufferlist::iterator ebliter = bl.begin();
+ omap_section os;
+ os.decode(ebliter);
+
+ if (debug)
+ cerr << "\tomap: size " << os.omap.size() << std::endl;
+ t->omap_setkeys(coll, hoid, os.omap);
+ return 0;
+}
+
+int get_object(ObjectStore *store, coll_t coll, bufferlist &bl)
+{
+ ObjectStore::Transaction tran;
+ ObjectStore::Transaction *t = &tran;
+ bufferlist::iterator ebliter = bl.begin();
+ object_begin ob;
+ ob.decode(ebliter);
+ OSDriver driver(
+ store,
+ coll_t(),
+ OSD::make_snapmapper_oid());
+ spg_t pg;
+ coll.is_pg_prefix(pg);
+ SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
+
+ t->touch(coll, ob.hoid);
+
+ cout << "Write " << ob.hoid << std::endl;
+
+ bufferlist ebl;
+ bool done = false;
+ while(!done) {
+ sectiontype_t type;
+ int ret = read_section(file_fd, &type, &ebl);
+ if (ret)
+ return ret;
+
+ //cout << "\tdo_object: Section type " << hex << type << dec << std::endl;
+ //cout << "\t\tsection size " << ebl.length() << std::endl;
+ if (type >= END_OF_TYPES) {
+ cout << "Skipping unknown object section type" << std::endl;
+ continue;
+ }
+ switch(type) {
+ case TYPE_DATA:
+ ret = get_data(store, coll, ob.hoid, t, ebl);
+ if (ret) return ret;
+ break;
+ case TYPE_ATTRS:
+ ret = get_attrs(store, coll, ob.hoid, t, ebl, driver, mapper);
+ if (ret) return ret;
+ break;
+ case TYPE_OMAP_HDR:
+ ret = get_omap_hdr(store, coll, ob.hoid, t, ebl);
+ if (ret) return ret;
+ break;
+ case TYPE_OMAP:
+ ret = get_omap(store, coll, ob.hoid, t, ebl);
+ if (ret) return ret;
+ break;
+ case TYPE_OBJECT_END:
+ done = true;
+ break;
+ default:
+ return EFAULT;
+ }
+ }
+ store->apply_transaction(*t);
+ return 0;
+}
+
+int get_pg_metadata(ObjectStore *store, coll_t coll, bufferlist &bl)
+{
+ ObjectStore::Transaction tran;
+ ObjectStore::Transaction *t = &tran;
+ bufferlist::iterator ebliter = bl.begin();
+ metadata_section ms;
+ ms.decode(ebliter);
+
+#if DIAGNOSTIC
+ Formatter *formatter = new JSONFormatter(true);
+ cout << "struct_v " << (int)ms.struct_ver << std::endl;
+ cout << "epoch " << ms.map_epoch << std::endl;
+ formatter->open_object_section("info");
+ ms.info.dump(formatter);
+ formatter->close_section();
+ formatter->flush(cout);
+ cout << std::endl;
+
+ formatter->open_object_section("log");
+ ms.log.dump(formatter);
+ formatter->close_section();
+ formatter->flush(cout);
+ cout << std::endl;
+#endif
+
+ coll_t newcoll(ms.info.pgid);
+ t->collection_rename(coll, newcoll);
+
+ int ret = write_pg(*t, ms.map_epoch, ms.info, ms.log, ms.struct_ver);
+ if (ret) return ret;
+
+ store->apply_transaction(*t);
+
+ return 0;
+}
+
+int do_import(ObjectStore *store, OSDSuperblock& sb)
+{
+ bufferlist ebl;
+ pg_info_t info;
+ PGLog::IndexedLog log;
+
+ uint64_t next_removal_seq = 0; //My local seq
+ finish_remove_pgs(store, &next_removal_seq);
+
+ int ret = sh.read_super();
+ if (ret)
+ return ret;
+
+ if (sh.magic != super_header::super_magic) {
+ cerr << "Invalid magic number" << std::endl;
+ return EFAULT;
+ }
+
+ if (sh.version > super_header::super_ver) {
+ cerr << "Can't handle export format version=" << sh.version << std::endl;
+ return EINVAL;
+ }
+
+ //First section must be TYPE_PG_BEGIN
+ sectiontype_t type;
+ ret = read_section(file_fd, &type, &ebl);
+ if (ret)
+ return ret;
+ if (type != TYPE_PG_BEGIN) {
+ return EFAULT;
+ }
+
+ bufferlist::iterator ebliter = ebl.begin();
+ pg_begin pgb;
+ pgb.decode(ebliter);
+ spg_t pgid = pgb.pgid;
+
+ if (debug) {
+ cerr << "Exported features: " << pgb.superblock.compat_features << std::endl;
+ }
+ if (sb.compat_features.compare(pgb.superblock.compat_features) == -1) {
+ cerr << "Export has incompatible features set "
+ << pgb.superblock.compat_features << std::endl;
+ return 1;
+ }
+
+ log_oid = OSD::make_pg_log_oid(pgid);
+ biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
+
+ //Check for PG already present.
+ coll_t coll(pgid);
+ if (store->collection_exists(coll)) {
+ cerr << "pgid " << pgid << " already exists" << std::endl;
+ return 1;
+ }
+
+ //Switch to collection which will be removed automatically if
+ //this program is interupted.
+ coll_t rmcoll = coll_t::make_removal_coll(next_removal_seq, pgid);
+ ObjectStore::Transaction *t = new ObjectStore::Transaction;
+ t->create_collection(rmcoll);
+ store->apply_transaction(*t);
+ delete t;
+
+ cout << "Importing pgid " << pgid << std::endl;
+
+ bool done = false;
+ bool found_metadata = false;
+ while(!done) {
+ ret = read_section(file_fd, &type, &ebl);
+ if (ret)
+ return ret;
+
+ //cout << "do_import: Section type " << hex << type << dec << std::endl;
+ if (type >= END_OF_TYPES) {
+ cout << "Skipping unknown section type" << std::endl;
+ continue;
+ }
+ switch(type) {
+ case TYPE_OBJECT_BEGIN:
+ ret = get_object(store, rmcoll, ebl);
+ if (ret) return ret;
+ break;
+ case TYPE_PG_METADATA:
+ ret = get_pg_metadata(store, rmcoll, ebl);
+ if (ret) return ret;
+ found_metadata = true;
+ break;
+ case TYPE_PG_END:
+ done = true;
+ break;
+ default:
+ return EFAULT;
+ }
+ }
+
+ if (!found_metadata) {
+ cerr << "Missing metadata section" << std::endl;
+ return EFAULT;
+ }
+
+ return 0;
+}
+
+int do_list(ObjectStore *store, coll_t coll, Formatter *formatter)
+{
+ vector<ghobject_t> objects;
+ ghobject_t next;
+ while (!next.is_max()) {
+ int r = store->collection_list_partial(coll, next, 200, 300, 0,
+ &objects, &next);
+ if (r < 0)
+ return r;
+ for (vector<ghobject_t>::iterator i = objects.begin();
+ i != objects.end(); ++i) {
+
+ formatter->open_object_section("list");
+ i->dump(formatter);
+ formatter->close_section();
+ formatter->flush(cout);
+ cout << std::endl;
+ }
+ }
+ return 0;
+}
+
+int do_remove_object(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
+{
+ spg_t pg;
+ coll.is_pg_prefix(pg);
+ OSDriver driver(
+ store,
+ coll_t(),
+ OSD::make_snapmapper_oid());
+ SnapMapper mapper(&driver, 0, 0, 0, pg.shard);
+ struct stat st;
+
+ int r = store->stat(coll, ghobj, &st);
+ if (r < 0) {
+ cerr << "remove: " << cpp_strerror(-r) << std::endl;
+ return r;
+ }
+
+ ObjectStore::Transaction *t = new ObjectStore::Transaction;
+ OSDriver::OSTransaction _t(driver.get_transaction(t));
+ cout << "remove " << ghobj << std::endl;
+ r = mapper.remove_oid(ghobj.hobj, &_t);
+ if (r != 0 && r != -ENOENT) {
+ cerr << "remove_oid returned " << cpp_strerror(-r) << std::endl;
+ return r;
+ }
+
+ t->remove(coll, ghobj);
+
+ store->apply_transaction(*t);
+ delete t;
+ return 0;
+}
+
+int do_list_attrs(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
+{
+ map<string,bufferptr> aset;
+ int r = store->getattrs(coll, ghobj, aset);
+ if (r < 0) {
+ cerr << "getattrs: " << cpp_strerror(-r) << std::endl;
+ return r;
+ }
+
+ for (map<string,bufferptr>::iterator i = aset.begin();i != aset.end(); ++i) {
+ string key(i->first);
+ if (outistty)
+ cleanbin(key);
+ cout << key << std::endl;
+ }
+ return 0;
+}
+
+int do_list_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
+{
+ ObjectMap::ObjectMapIterator iter = store->get_omap_iterator(coll, ghobj);
+ if (!iter) {
+ cerr << "omap_get_iterator: " << cpp_strerror(ENOENT) << std::endl;
+ return -ENOENT;
+ }
+ iter->seek_to_first();
+ map<string, bufferlist> oset;
+ while(iter->valid()) {
+ get_omap_batch(iter, oset);
+
+ for (map<string,bufferlist>::iterator i = oset.begin();i != oset.end(); ++i) {
+ string key(i->first);
+ if (outistty)
+ cleanbin(key);
+ cout << key << std::endl;
+ }
+ }
+ return 0;
+}
+
+int do_get_bytes(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
+{
+ struct stat st;
+ mysize_t total;
+
+ int ret = store->stat(coll, ghobj, &st);
+ if (ret < 0) {
+ cerr << "get-bytes: " << cpp_strerror(-ret) << std::endl;
+ return 1;
+ }
+
+ total = st.st_size;
+ if (debug)
+ cerr << "size=" << total << std::endl;
+
+ uint64_t offset = 0;
+ bufferlist rawdatabl;
+ while(total > 0) {
+ rawdatabl.clear();
+ mysize_t len = max_read;
+ if (len > total)
+ len = total;
+
+ ret = store->read(coll, ghobj, offset, len, rawdatabl);
+ if (ret < 0)
+ return ret;
+ if (ret == 0)
+ return -EINVAL;
+
+ if (debug)
+ cerr << "data section offset=" << offset << " len=" << len << std::endl;
+
+ total -= ret;
+ offset += ret;
+
+ ret = write(fd, rawdatabl.c_str(), ret);
+ if (ret == -1) {
+ perror("write");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+int do_set_bytes(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
+{
+ ObjectStore::Transaction tran;
+ ObjectStore::Transaction *t = &tran;
+
+ if (debug)
+ cerr << "Write " << ghobj << std::endl;
+
+ t->touch(coll, ghobj);
+ t->truncate(coll, ghobj, 0);
+
+ uint64_t offset = 0;
+ bufferlist rawdatabl;
+ do {
+ rawdatabl.clear();
+ ssize_t bytes = rawdatabl.read_fd(fd, max_read);
+ if (bytes < 0) {
+ cerr << "read_fd error " << cpp_strerror(-bytes) << std::endl;
+ return 1;
+ }
+
+ if (bytes == 0)
+ break;
+
+ if (debug)
+ cerr << "\tdata: offset " << offset << " bytes " << bytes << std::endl;
+ t->write(coll, ghobj, offset, bytes, rawdatabl);
+
+ offset += bytes;
+ // XXX: Should we apply_transaction() every once in a while for very large files
+ } while(true);
+
+ store->apply_transaction(*t);
+ return 0;
+}
+
+int do_get_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
+{
+ bufferptr bp;
+
+ int r = store->getattr(coll, ghobj, key.c_str(), bp);
+ if (r < 0) {
+ cerr << "getattr: " << cpp_strerror(-r) << std::endl;
+ return r;
+ }
+
+ string value(bp.c_str(), bp.length());
+ if (outistty) {
+ cleanbin(value);
+ value.push_back('\n');
+ }
+ cout << value;
+
+ return 0;
+}
+
+int do_set_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key, int fd)
+{
+ ObjectStore::Transaction tran;
+ ObjectStore::Transaction *t = &tran;
+ bufferlist bl;
+
+ if (debug)
+ cerr << "Setattr " << ghobj << std::endl;
+
+ if (get_fd_data(fd, bl))
+ return 1;
+
+ t->touch(coll, ghobj);
+
+ t->setattr(coll, ghobj, key, bl);
+
+ store->apply_transaction(*t);
+ return 0;
+}
+
+int do_rm_attr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
+{
+ ObjectStore::Transaction tran;
+ ObjectStore::Transaction *t = &tran;
+
+ if (debug)
+ cerr << "Rmattr " << ghobj << std::endl;
+
+ t->rmattr(coll, ghobj, key);
+
+ store->apply_transaction(*t);
+ return 0;
+}
+
+int do_get_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
+{
+ set<string> keys;
+ map<string, bufferlist> out;
+
+ keys.insert(key);
+
+ int r = store->omap_get_values(coll, ghobj, keys, &out);
+ if (r < 0) {
+ cerr << "omap_get_values: " << cpp_strerror(-r) << std::endl;
+ return r;
+ }
+
+ if (out.empty()) {
+ cerr << "Key not found" << std::endl;
+ return -ENOENT;
+ }
+
+ assert(out.size() == 1);
+
+ bufferlist bl = out.begin()->second;
+ string value(bl.c_str(), bl.length());
+ if (outistty) {
+ cleanbin(value);
+ value.push_back('\n');
+ }
+ cout << value;
+
+ return 0;
+}
+
+int do_set_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key, int fd)
+{
+ ObjectStore::Transaction tran;
+ ObjectStore::Transaction *t = &tran;
+ map<string, bufferlist> attrset;
+ bufferlist valbl;
+
+ if (debug)
+ cerr << "Set_omap " << ghobj << std::endl;
+
+ if (get_fd_data(fd, valbl))
+ return 1;
+
+ attrset.insert(pair<string, bufferlist>(key, valbl));
+
+ t->touch(coll, ghobj);
+
+ t->omap_setkeys(coll, ghobj, attrset);
+
+ store->apply_transaction(*t);
+ return 0;
+}
+
+int do_rm_omap(ObjectStore *store, coll_t coll, ghobject_t &ghobj, string key)
+{
+ ObjectStore::Transaction tran;
+ ObjectStore::Transaction *t = &tran;
+ set<string> keys;
+
+ keys.insert(key);
+
+ if (debug)
+ cerr << "Rm_omap " << ghobj << std::endl;
+
+ t->omap_rmkeys(coll, ghobj, keys);
+
+ store->apply_transaction(*t);
+ return 0;
+}
+
+int do_get_omaphdr(ObjectStore *store, coll_t coll, ghobject_t &ghobj)
+{
+ bufferlist hdrbl;
+
+ int r = store->omap_get_header(coll, ghobj, &hdrbl, true);
+ if (r < 0) {
+ cerr << "omap_get_header: " << cpp_strerror(-r) << std::endl;
+ return r;
+ }
+
+ string header(hdrbl.c_str(), hdrbl.length());
+ if (outistty) {
+ cleanbin(header);
+ header.push_back('\n');
+ }
+ cout << header;
+
+ return 0;
+}
+
+int do_set_omaphdr(ObjectStore *store, coll_t coll, ghobject_t &ghobj, int fd)
+{
+ ObjectStore::Transaction tran;
+ ObjectStore::Transaction *t = &tran;
+ bufferlist hdrbl;
+
+ if (debug)
+ cerr << "Omap_setheader " << ghobj << std::endl;
+
+ if (get_fd_data(fd, hdrbl))
+ return 1;
+
+ t->touch(coll, ghobj);
+
+ t->omap_setheader(coll, ghobj, hdrbl);
+
+ store->apply_transaction(*t);
+ return 0;
+}
+
+void usage(po::options_description &desc)
+{
+ cerr << std::endl;
+ cerr << desc << std::endl;
+ cerr << std::endl;
+ cerr << "Positional syntax:" << std::endl;
+ cerr << std::endl;
+ cerr << "(requires --filestore-path, --journal-path and --pgid to be specified)" << std::endl;
+ cerr << "(optional [file] argument will read stdin or write stdout if not specified or if '-' specified)" << std::endl;
+ cerr << "ceph-filestore-dump ... <object> (get|set)-bytes [file]" << std::endl;
+ cerr << "ceph-filestore-dump ... <object> (set-(attr|omap) <key> [file]" << std::endl;
+ cerr << "ceph-filestore-dump ... <object> (set-omaphdr) [file]" << std::endl;
+ cerr << "ceph-filestore-dump ... <object> (get|rm)-(attr|omap) <key>" << std::endl;
+ cerr << "ceph-filestore-dump ... <object> (get-omaphdr)" << std::endl;
+ cerr << "ceph-filestore-dump ... <object> list-attrs" << std::endl;
+ cerr << "ceph-filestore-dump ... <object> list-omap" << std::endl;
+ cerr << "ceph-filestore-dump ... <object> remove" << std::endl;
+ cerr << std::endl;
+ exit(1);
+}
+
+int main(int argc, char **argv)
+{
+ string fspath, jpath, pgidstr, type, file, object, objcmd, arg1, arg2;
+ ghobject_t ghobj;
+
+ po::options_description desc("Allowed options");
+ desc.add_options()
+ ("help", "produce help message")
+ ("filestore-path", po::value<string>(&fspath),
+ "path to filestore directory, mandatory")
+ ("journal-path", po::value<string>(&jpath),
+ "path to journal, mandatory")
+ ("pgid", po::value<string>(&pgidstr),
+ "PG id, mandatory except for import")
+ ("type", po::value<string>(&type),
+ "Arg is one of [info, log, remove, export, import, list]")
+ ("file", po::value<string>(&file),
+ "path of file to export or import")
+ ("debug", "Enable diagnostic output to stderr")
+ ("skip-journal-replay", "Disable journal replay")
+ ("skip-mount-omap", "Disable mounting of omap")
+ ;
+
+ po::options_description positional("Positional options");
+ positional.add_options()
+ ("object", po::value<string>(&object), "ghobject in json")
+ ("objcmd", po::value<string>(&objcmd), "command [(get|set)-bytes, (get|set|rm)-(attr|omap), list-attrs, list-omap, remove]")
+ ("arg1", po::value<string>(&arg1), "arg1 based on cmd")
+ ("arg2", po::value<string>(&arg2), "arg2 based on cmd")
+ ;
+
+ po::options_description all("All options");
+ all.add(desc).add(positional);
+
+ po::positional_options_description pd;
+ pd.add("object", 1).add("objcmd", 1).add("arg1", 1).add("arg2", 1);
+
+ po::variables_map vm;
+ po::parsed_options parsed =
+ po::command_line_parser(argc, argv).options(all).allow_unregistered().positional(pd).run();
+ po::store( parsed, vm);
+ try {
+ po::notify(vm);
+ }
+ catch(...) {
+ usage(desc);
+ }
+
+ if (vm.count("help")) {
+ usage(desc);
+ }
+
+ if (!vm.count("filestore-path")) {
+ cerr << "Must provide --filestore-path" << std::endl;
+ usage(desc);
+ }
+ if (!vm.count("journal-path")) {
+ cerr << "Must provide --journal-path" << std::endl;
+ usage(desc);
+ }
+ if (vm.count("object") && !vm.count("objcmd")) {
+ cerr << "Invalid syntax, missing command" << std::endl;
+ usage(desc);
+ }
+ if (!vm.count("type") && !(vm.count("object") && vm.count("objcmd"))) {
+ cerr << "Must provide --type or object command..."
+ << std::endl;
+ usage(desc);
+ }
+ if (vm.count("type") && vm.count("object")) {
+ cerr << "Can't specify both --type and object command syntax" << std::endl;
+ usage(desc);
+ }
+ if (type != "import" && !vm.count("pgid")) {
+ cerr << "Must provide pgid" << std::endl;
+ usage(desc);
+ }
+
+ if (vm.count("object")) {
+ json_spirit::Value v;
+ try {
+ if (!json_spirit::read(object, v))
+ throw std::runtime_error("bad json");
+ ghobj.decode(v);
+ } catch (std::runtime_error& e) {
+ cerr << "error parsing offset: " << e.what() << std::endl;
+ exit(1);
+ }
+ }
+
+ outistty = isatty(STDOUT_FILENO);
+
+ file_fd = fd_none;
+ if (type == "export") {
+ if (!vm.count("file")) {
+ if (outistty) {
+ cerr << "stdout is a tty and no --file option specified" << std::endl;
+ exit(1);
+ }
+ file_fd = STDOUT_FILENO;
+ } else {
+ file_fd = open(file.c_str(), O_WRONLY|O_CREAT|O_TRUNC, 0666);
+ }
+ } else if (type == "import") {
+ if (!vm.count("file")) {
+ if (isatty(STDIN_FILENO)) {
+ cerr << "stdin is a tty and no --file option specified" << std::endl;
+ exit(1);
+ }
+ file_fd = STDIN_FILENO;
+ } else {
+ file_fd = open(file.c_str(), O_RDONLY);
+ }
+ }
+
+ if (vm.count("file") && file_fd == fd_none) {
+ cerr << "--file option only applies to import or export" << std::endl;
+ return 1;
+ }
+
+ if (file_fd != fd_none && file_fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ if ((fspath.length() == 0 || jpath.length() == 0) ||
+ (type != "import" && pgidstr.length() == 0)) {
+ cerr << "Invalid params" << std::endl;
+ return 1;
+ }
+
+ if (type == "import" && pgidstr.length()) {
+ cerr << "--pgid option invalid with import" << std::endl;
+ return 1;
+ }
+
+ vector<const char *> ceph_options, def_args;
+ vector<string> ceph_option_strings = po::collect_unrecognized(
+ parsed.options, po::include_positional);
+ ceph_options.reserve(ceph_option_strings.size());
+ for (vector<string>::iterator i = ceph_option_strings.begin();
+ i != ceph_option_strings.end();
+ ++i) {
+ ceph_options.push_back(i->c_str());
+ }
+
+ if (!vm.count("debug")) {
+ debug = false;
+ } else {
+ debug = true;
+ }
+
+ osflagbits_t flags = 0;
+ if (vm.count("skip-journal-replay"))
+ flags |= SKIP_JOURNAL_REPLAY;
+ if (vm.count("skip-mount-omap"))
+ flags |= SKIP_MOUNT_OMAP;
+
+ global_init(
+ &def_args, ceph_options, CEPH_ENTITY_TYPE_OSD,
+ CODE_ENVIRONMENT_UTILITY_NODOUT, 0);
+ //CINIT_FLAG_NO_DEFAULT_CONFIG_FILE);
+ common_init_finish(g_ceph_context);
+ g_conf = g_ceph_context->_conf;
+ if (debug) {
+ g_conf->set_val_or_die("log_to_stderr", "true");
+ g_conf->set_val_or_die("err_to_stderr", "true");
+ }
+ g_conf->apply_changes(NULL);
+
+ //Verify that fspath really is an osd store
+ struct stat st;
+ if (::stat(fspath.c_str(), &st) == -1) {
+ perror("fspath");
+ invalid_path(fspath);
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ invalid_path(fspath);
+ }
+ string check = fspath + "/whoami";
+ if (::stat(check.c_str(), &st) == -1) {
+ perror("whoami");
+ invalid_path(fspath);
+ }
+ if (!S_ISREG(st.st_mode)) {
+ invalid_path(fspath);
+ }
+ check = fspath + "/current";
+ if (::stat(check.c_str(), &st) == -1) {
+ perror("current");
+ invalid_path(fspath);
+ }
+ if (!S_ISDIR(st.st_mode)) {
+ invalid_path(fspath);
+ }
+
+ spg_t pgid;
+ if (pgidstr.length() && !pgid.parse(pgidstr.c_str())) {
+ cerr << "Invalid pgid '" << pgidstr << "' specified" << std::endl;
+ return 1;
+ }
+
+ ObjectStore *fs = ObjectStore::create(NULL, "filestore", fspath, jpath, flags);
+
+ int r = fs->mount();
+ if (r < 0) {
+ if (r == -EBUSY) {
+ cerr << "OSD has the store locked" << std::endl;
+ } else {
+ cerr << "Mount failed with '" << cpp_strerror(-r) << "'" << std::endl;
+ }
+ return 1;
+ }
+
+ bool fs_sharded_objects = fs->get_allow_sharded_objects();
+
+ int ret = 0;
+ vector<coll_t> ls;
+ vector<coll_t>::iterator it;
+ CompatSet supported;
+
+#ifdef INTERNAL_TEST
+ supported = get_test_compat_set();
+#else
+ supported = OSD::get_osd_compat_set();
+#endif
+
+ bufferlist bl;
+ OSDSuperblock superblock;
+ bufferlist::iterator p;
+ r = fs->read(META_COLL, OSD_SUPERBLOCK_POBJECT, 0, 0, bl);
+ if (r < 0) {
+ cerr << "Failure to read OSD superblock error= " << r << std::endl;
+ goto out;
+ }
+
+ p = bl.begin();
+ ::decode(superblock, p);
+
+#ifdef INTERNAL_TEST2
+ fs->set_allow_sharded_objects();
+ assert(fs->get_allow_sharded_objects());
+ fs_sharded_objects = true;
+ superblock.compat_features.incompat.insert(CEPH_OSD_FEATURE_INCOMPAT_SHARDS);
+#endif
+
+ if (debug) {
+ cerr << "Supported features: " << supported << std::endl;
+ cerr << "On-disk features: " << superblock.compat_features << std::endl;
+ }
+ if (supported.compare(superblock.compat_features) == -1) {
+ cerr << "On-disk OSD incompatible features set "
+ << superblock.compat_features << std::endl;
+ ret = EINVAL;
+ goto out;
+ }
+
+ // If there was a crash as an OSD was transitioning to sharded objects
+ // and hadn't completed a set_allow_sharded_objects().
+ // This utility does not want to attempt to finish that transition.
+ if (superblock.compat_features.incompat.contains(CEPH_OSD_FEATURE_INCOMPAT_SHARDS) != fs_sharded_objects) {
+ // An OSD should never have call set_allow_sharded_objects() before
+ // updating its own OSD features.
+ if (fs_sharded_objects)
+ cerr << "FileStore sharded but OSD not set, Corruption?" << std::endl;
+ else
+ cerr << "Found incomplete transition to sharded objects" << std::endl;
+ ret = EINVAL;
+ goto out;
+ }
+
+ if (type == "import") {
+
+ try {
+ ret = do_import(fs, superblock);
+ }
+ catch (const buffer::error &e) {
+ cerr << "do_import threw exception error " << e.what() << std::endl;
+ ret = EFAULT;
+ }
+ if (ret == EFAULT) {
+ cerr << "Corrupt input for import" << std::endl;
+ }
+ if (ret == 0)
+ cout << "Import successful" << std::endl;
+ goto out;
+ }
+
+ log_oid = OSD::make_pg_log_oid(pgid);
+ biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
+
+ if (type == "remove") {
+ uint64_t next_removal_seq = 0; //My local seq
+ finish_remove_pgs(fs, &next_removal_seq);
+ int r = initiate_new_remove_pg(fs, pgid, &next_removal_seq);
+ if (r) {
+ cerr << "PG '" << pgid << "' not found" << std::endl;
+ ret = 1;
+ goto out;
+ }
+ finish_remove_pgs(fs, &next_removal_seq);
+ cout << "Remove successful" << std::endl;
+ goto out;
+ }
+
+ r = fs->list_collections(ls);
+ if (r < 0) {
+ cerr << "failed to list pgs: " << cpp_strerror(-r) << std::endl;
+ ret = 1;
+ goto out;
+ }
+
+ for (it = ls.begin(); it != ls.end(); ++it) {
+ snapid_t snap;
+ spg_t tmppgid;
+
+ if (!it->is_pg(tmppgid, snap)) {
+ continue;
+ }
+
+ if (it->is_temp(tmppgid)) {
+ continue;
+ }
+
+ if (tmppgid != pgid) {
+ continue;
+ }
+ if (snap != CEPH_NOSNAP && debug) {
+ cerr << "skipping snapped dir " << *it
+ << " (pg " << pgid << " snap " << snap << ")" << std::endl;
+ continue;
+ }
+
+ //Found!
+ break;
+ }
+
+ epoch_t map_epoch;
+// The following code for export, info, log require omap or !skip-mount-omap
+ if (it != ls.end()) {
+
+ coll_t coll = *it;
+
+ if (vm.count("objcmd")) {
+ ret = 0;
+ if (objcmd == "remove") {
+ int r = do_remove_object(fs, coll, ghobj);
+ if (r) {
+ ret = 1;
+ }
+ goto out;
+ } else if (objcmd == "list-attrs") {
+ int r = do_list_attrs(fs, coll, ghobj);
+ if (r) {
+ ret = 1;
+ }
+ goto out;
+ } else if (objcmd == "list-omap") {
+ int r = do_list_omap(fs, coll, ghobj);
+ if (r) {
+ ret = 1;
+ }
+ goto out;
+ } else if (objcmd == "get-bytes" || objcmd == "set-bytes") {
+ int r;
+ if (objcmd == "get-bytes") {
+ int fd;
+ if (vm.count("arg1") == 0 || arg1 == "-") {
+ fd = STDOUT_FILENO;
+ } else {
+ fd = open(arg1.c_str(), O_WRONLY|O_TRUNC|O_CREAT|O_EXCL|O_LARGEFILE, 0666);
+ if (fd == -1) {
+ cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
+ ret = 1;
+ goto out;
+ }
+ }
+ r = do_get_bytes(fs, coll, ghobj, fd);
+ if (fd != STDOUT_FILENO)
+ close(fd);
+ } else {
+ int fd;
+ if (vm.count("arg1") == 0 || arg1 == "-") {
+ fd = STDIN_FILENO;
+ } else {
+ fd = open(arg1.c_str(), O_RDONLY|O_LARGEFILE, 0666);
+ if (fd == -1) {
+ cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
+ ret = 1;
+ goto out;
+ }
+ }
+ r = do_set_bytes(fs, coll, ghobj, fd);
+ if (fd != STDIN_FILENO)
+ close(fd);
+ }
+ if (r)
+ ret = 1;
+ goto out;
+ } else if (objcmd == "get-attr") {
+ if (vm.count("arg1") == 0)
+ usage(desc);
+ r = do_get_attr(fs, coll, ghobj, arg1);
+ if (r)
+ ret = 1;
+ goto out;
+ } else if (objcmd == "set-attr") {
+ if (vm.count("arg1") == 0)
+ usage(desc);
+
+ int fd;
+ if (vm.count("arg2") == 0 || arg2 == "-") {
+ fd = STDIN_FILENO;
+ } else {
+ fd = open(arg2.c_str(), O_RDONLY|O_LARGEFILE, 0666);
+ if (fd == -1) {
+ cerr << "open " << arg2 << " " << cpp_strerror(errno) << std::endl;
+ ret = 1;
+ goto out;
+ }
+ }
+ r = do_set_attr(fs, coll, ghobj, arg1, fd);
+ if (fd != STDIN_FILENO)
+ close(fd);
+ if (r)
+ ret = 1;
+ goto out;
+ } else if (objcmd == "rm-attr") {
+ if (vm.count("arg1") == 0)
+ usage(desc);
+ r = do_rm_attr(fs, coll, ghobj, arg1);
+ if (r)
+ ret = 1;
+ goto out;
+ } else if (objcmd == "get-omap") {
+ if (vm.count("arg1") == 0)
+ usage(desc);
+ r = do_get_omap(fs, coll, ghobj, arg1);
+ if (r)
+ ret = 1;
+ goto out;
+ } else if (objcmd == "set-omap") {
+ if (vm.count("arg1") == 0)
+ usage(desc);
+
+ int fd;
+ if (vm.count("arg2") == 0 || arg2 == "-") {
+ fd = STDIN_FILENO;
+ } else {
+ fd = open(arg2.c_str(), O_RDONLY|O_LARGEFILE, 0666);
+ if (fd == -1) {
+ cerr << "open " << arg2 << " " << cpp_strerror(errno) << std::endl;
+ ret = 1;
+ goto out;
+ }
+ }
+ r = do_set_omap(fs, coll, ghobj, arg1, fd);
+ if (fd != STDIN_FILENO)
+ close(fd);
+ if (r)
+ ret = 1;
+ goto out;
+ } else if (objcmd == "rm-omap") {
+ if (vm.count("arg1") == 0)
+ usage(desc);
+ r = do_rm_omap(fs, coll, ghobj, arg1);
+ if (r)
+ ret = 1;
+ goto out;
+ } else if (objcmd == "get-omaphdr") {
+ if (vm.count("arg1"))
+ usage(desc);
+ r = do_get_omaphdr(fs, coll, ghobj);
+ if (r)
+ ret = 1;
+ goto out;
+ } else if (objcmd == "set-omaphdr") {
+ // Extra arg
+ if (vm.count("arg2"))
+ usage(desc);
+ int fd;
+ if (vm.count("arg1") == 0 || arg1 == "-") {
+ fd = STDIN_FILENO;
+ } else {
+ fd = open(arg1.c_str(), O_RDONLY|O_LARGEFILE, 0666);
+ if (fd == -1) {
+ cerr << "open " << arg1 << " " << cpp_strerror(errno) << std::endl;
+ ret = 1;
+ goto out;
+ }
+ }
+ r = do_set_omaphdr(fs, coll, ghobj, fd);
+ if (fd != STDIN_FILENO)
+ close(fd);
+ if (r)
+ ret = 1;
+ goto out;
+ }
+ cerr << "Unknown object command '" << objcmd << "'" << std::endl;
+ usage(desc);
+ }
+
+ if (type == "list") {
+ Formatter *formatter = new JSONFormatter(false);
+ r = do_list(fs, coll, formatter);
+ if (r) {
+ cerr << "do_list failed with " << r << std::endl;
+ ret = 1;
+ }
+ goto out;
+ }
+
+ Formatter *formatter = new JSONFormatter(true);
+ bufferlist bl;
+ map_epoch = PG::peek_map_epoch(fs, coll, infos_oid, &bl);
+ if (debug)
+ cerr << "map_epoch " << map_epoch << std::endl;
+
+ pg_info_t info(pgid);
+ map<epoch_t,pg_interval_t> past_intervals;
+ hobject_t biginfo_oid = OSD::make_pg_biginfo_oid(pgid);
+ interval_set<snapid_t> snap_collections;
+
+ __u8 struct_ver;
+ r = PG::read_info(fs, coll, bl, info, past_intervals, biginfo_oid,
+ infos_oid, snap_collections, struct_ver);
+ if (r < 0) {
+ cerr << "read_info error " << cpp_strerror(-r) << std::endl;
+ ret = 1;
+ goto out;
+ }
+ if (debug)
+ cerr << "struct_v " << (int)struct_ver << std::endl;
+
+ if (type == "export") {
+ ret = do_export(fs, coll, pgid, info, map_epoch, struct_ver, superblock);
+ if (ret == 0 && file_fd != STDOUT_FILENO)
+ cout << "Export successful" << std::endl;
+ } else if (type == "info") {
+ formatter->open_object_section("info");
+ info.dump(formatter);
+ formatter->close_section();
+ formatter->flush(cout);
+ cout << std::endl;
+ } else if (type == "log") {
+ PGLog::IndexedLog log;
+ pg_missing_t missing;
+ ret = get_log(fs, coll, pgid, info, log, missing);
+ if (ret > 0)
+ goto out;
+
+ formatter->open_object_section("log");
+ log.dump(formatter);
+ formatter->close_section();
+ formatter->flush(cout);
+ cout << std::endl;
+ formatter->open_object_section("missing");
+ missing.dump(formatter);
+ formatter->close_section();
+ formatter->flush(cout);
+ cout << std::endl;
+ } else {
+ cerr << "Must provide --type (info, log, remove, export, import, list)"
+ << std::endl;
+ usage(desc);
+ }
+ } else {
+ cerr << "PG '" << pgid << "' not found" << std::endl;
+ ret = 1;
+ }
+
+out:
+ if (fs->umount() < 0) {
+ cerr << "umount failed" << std::endl;
+ return 1;
+ }
+
+ return (ret != 0);
+}