示例#1
0
def tombstone(uid):
    """
    Handle all tombstone operations.

    The only allowed methods are POST and DELETE; any other verb will return a
    405.
    """
    try:
        rsrc_api.get(uid)
    except exc.TombstoneError as e:
        if request.method == 'DELETE':
            if e.uid == uid:
                rsrc_api.delete(uid, False)
                return '', 204
            else:
                return _tombstone_response(e, uid)
        elif request.method == 'POST':
            if e.uid == uid:
                rsrc_uri = rsrc_api.resurrect(uid)
                headers = {'Location': rsrc_uri}
                return rsrc_uri, 201, headers
            else:
                return _tombstone_response(e, uid)
        else:
            return 'Method Not Allowed.', 405
    except exc.ResourceNotExistsError as e:
        return str(e), 404
    else:
        return '', 404
示例#2
0
def delete_resource(uid):
    '''
    Delete a resource and optionally leave a tombstone.

    This behaves differently from FCREPO. A tombstone indicated that the
    resource is no longer available at its current location, but its historic
    snapshots still are. Also, deleting a resource with a tombstone creates
    one more version snapshot of the resource prior to being deleted.

    In order to completely wipe out all traces of a resource, the tombstone
    must be deleted as well, or the `Prefer:no-tombstone` header can be used.
    The latter will forget (completely delete) the resource immediately.
    '''
    headers = std_headers

    if 'prefer' in request.headers:
        prefer = g.tbox.parse_rfc7240(request.headers['prefer'])
        leave_tstone = 'no-tombstone' not in prefer
    else:
        leave_tstone = True

    try:
        rsrc_api.delete(uid, leave_tstone)
    except ResourceNotExistsError as e:
        return str(e), 404
    except TombstoneError as e:
        return _tombstone_response(e, uid)

    return '', 204, headers
示例#3
0
    def test_hard_delete_descendants(self):
        """
        Forget a resource with all its descendants.
        """
        uid = '/test_hard_delete_descendants01'
        rsrc_api.create_or_replace(uid)
        for i in range(1, 4):
            rsrc_api.create_or_replace('{}/child{}'.format(uid, i))
            for j in range(i):
                rsrc_api.create_or_replace('{}/child{}/grandchild{}'.format(
                    uid, i, j))
        rsrc_api.delete(uid, False)
        with pytest.raises(ResourceNotExistsError):
            rsrc_api.get(uid)
        with pytest.raises(ResourceNotExistsError):
            rsrc_api.resurrect(uid)

        for i in range(1, 4):
            with pytest.raises(ResourceNotExistsError):
                rsrc_api.get('{}/child{}'.format(uid, i))
            with pytest.raises(ResourceNotExistsError):
                rsrc_api.resurrect('{}/child{}'.format(uid, i))

            for j in range(i):
                with pytest.raises(ResourceNotExistsError):
                    rsrc_api.get('{}/child{}/grandchild{}'.format(
                        uid, i, j))
                with pytest.raises(ResourceNotExistsError):
                    rsrc_api.resurrect('{}/child{}/grandchild{}'.format(
                        uid, i, j))
示例#4
0
 def test_soft_delete(self):
     """
     Soft-delete (bury) a resource.
     """
     uid = '/test_soft_delete01'
     rsrc_api.create_or_replace(uid)
     rsrc_api.delete(uid)
     with pytest.raises(TombstoneError):
         rsrc_api.get(uid)
示例#5
0
 def test_hard_delete(self):
     """
     Hard-delete (forget) a resource.
     """
     uid = '/test_hard_delete01'
     rsrc_api.create_or_replace(uid)
     rsrc_api.delete(uid, False)
     with pytest.raises(ResourceNotExistsError):
         rsrc_api.get(uid)
     with pytest.raises(ResourceNotExistsError):
         rsrc_api.resurrect(uid)
示例#6
0
    def test_resurrect(self):
        """
        Restore (resurrect) a soft-deleted resource.
        """
        uid = '/test_soft_delete02'
        rsrc_api.create_or_replace(uid)
        rsrc_api.delete(uid)
        rsrc_api.resurrect(uid)

        rsrc = rsrc_api.get(uid)
        with env.app_globals.rdf_store.txn_ctx():
            assert nsc['ldp'].Resource in rsrc.ldp_types
示例#7
0
 def test_delete_children(self):
     """
     Soft-delete a resource with children.
     """
     uid = '/test_soft_delete_children01'
     rsrc_api.create_or_replace(uid)
     for i in range(3):
         rsrc_api.create_or_replace('{}/child{}'.format(uid, i))
     rsrc_api.delete(uid)
     with pytest.raises(TombstoneError):
         rsrc_api.get(uid)
     for i in range(3):
         with pytest.raises(TombstoneError):
             rsrc_api.get('{}/child{}'.format(uid, i))
         # Cannot resurrect children of a tombstone.
         with pytest.raises(TombstoneError):
             rsrc_api.resurrect('{}/child{}'.format(uid, i))
示例#8
0
    def test_hard_delete_children(self):
        """
        Hard-delete (forget) a resource with its children.

        This uses fixtures from the previous test.
        """
        uid = '/test_hard_delete_children01'
        rsrc_api.create_or_replace(uid)
        for i in range(3):
            rsrc_api.create_or_replace('{}/child{}'.format(uid, i))
        rsrc_api.delete(uid, False)
        with pytest.raises(ResourceNotExistsError):
            rsrc_api.get(uid)
        with pytest.raises(ResourceNotExistsError):
            rsrc_api.resurrect(uid)

        for i in range(3):
            with pytest.raises(ResourceNotExistsError):
                rsrc_api.get('{}/child{}'.format(uid, i))
            with pytest.raises(ResourceNotExistsError):
                rsrc_api.resurrect('{}/child{}'.format(uid, i))
示例#9
0
def delete_resource(uid):
    """
    Delete a resource and optionally leave a tombstone.

    This behaves differently from FCREPO. A tombstone indicated that the
    resource is no longer available at its current location, but its historic
    snapshots still are. Also, deleting a resource with a tombstone creates
    one more version snapshot of the resource prior to being deleted.

    In order to completely wipe out all traces of a resource, the tombstone
    must be deleted as well, or the ``Prefer:no-tombstone`` header can be used.
    The latter will forget (completely delete) the resource immediately.
    """
    # Fist check if it's not a 404 or a 410.
    try:
        if not rsrc_api.exists(uid):
            return '', 404
    except exc.TombstoneError as e:
        return _tombstone_response(e, uid)

    # Then process the condition headers.
    cond_ret = _process_cond_headers(uid, request.headers, False)
    if cond_ret:
        return cond_ret

    headers = std_headers.copy()

    if 'prefer' in request.headers:
        prefer = toolbox.parse_rfc7240(request.headers['prefer'])
        leave_tstone = 'no-tombstone' not in prefer
    else:
        leave_tstone = True

    rsrc_api.delete(uid, leave_tstone)

    return '', 204, headers
示例#10
0
def run(mode, endpoint, count, parent, method, delete_container, graph_size,
        image_size, resource_type, plot):
    """
    Run the benchmark.
    """

    method = method.lower()
    if method not in ('post', 'put'):
        raise ValueError(f'Insertion method not supported: {method}')

    mode = mode.lower()
    if mode == 'ldp':
        parent = '{}/{}'.format(endpoint.strip('/'), parent.strip('/'))

        if delete_container:
            print('Removing previously existing container.')
            requests.delete(parent)
            requests.delete(f'{parent}/fcr:tombstone')
        requests.put(parent)

    elif mode == 'python':
        from lakesuperior import env_setup
        from lakesuperior.api import resource as rsrc_api

        if delete_container:
            try:
                print('Removing previously existing container.')
                rsrc_api.delete(parent, soft=False)
            except ResourceNotExistsError:
                pass
        rsrc_api.create_or_replace(parent)
    else:
        raise ValueError(f'Mode not supported: {mode}')

    if resource_type != 'r':
        # Set image parameters.
        ims = max(image_size - image_size % 8, 128)
        tn = ims // 32

    # URI used to establish an in-repo relationship. This is set to
    # the most recently created resource in each loop.
    ref = parent

    print(f'Inserting {count} children under {parent}.')

    wclock_start = arrow.utcnow()
    if plot:
        print('Results will be plotted.')
        # Plot coordinates: X is request count, Y is request timing.
        px = []
        py = []
        plt.xlabel('Requests')
        plt.ylabel('ms per request')
        plt.title('Lakesuperior / FCREPO Benchmark')

    try:
        for i in range(1, count + 1):
            if mode == 'ldp':
                dest = (f'{parent}/{uuid4()}' if method == 'put' else parent)
            else:
                dest = (path.join(parent, str(uuid4()))
                        if method == 'put' else parent)

            if resource_type == 'r' or (resource_type == 'b' and i % 2 == 0):
                data = random_graph(graph_size, ref)
                headers = {'content-type': 'text/turtle'}
            else:
                img = random_image(tn=tn, ims=ims)
                data = img['content']
                data.seek(0)
                headers = {
                    'content-type':
                    'image/png',
                    'content-disposition':
                    'attachment; filename="{}"'.format(uuid4())
                }

            # Start timing after generating the data.
            ckpt = arrow.utcnow()
            if i == 1:
                tcounter = ckpt - ckpt
                prev_tcounter = tcounter

            #import pdb; pdb.set_trace()
            ref = (_ingest_ldp(method, dest, data, headers, ref)
                   if mode == 'ldp' else _ingest_py(method, dest, data, ref))
            tcounter += (arrow.utcnow() - ckpt)

            if i % 10 == 0:
                avg10 = (tcounter - prev_tcounter) / 10
                print(f'Record: {i}\tTime elapsed: {tcounter}\t'
                      f'Per resource: {avg10}')
                prev_tcounter = tcounter

                if plot:
                    px.append(i)
                    # Divide by 1000 for µs → ms
                    py.append(avg10.microseconds // 1000)

    except KeyboardInterrupt:
        print('Interrupted after {} iterations.'.format(i))

    wclock = arrow.utcnow() - wclock_start
    print(f'Total elapsed time: {wclock}')
    print(f'Total time spent ingesting resources: {tcounter}')
    print(f'Average time per resource: {tcounter.total_seconds()/i}')

    if plot:
        if resource_type == 'r':
            type_label = 'LDP-RS'
        elif resource_type == 'n':
            type_label = 'LDP-NR'
        else:
            type_label = 'LDP-RS + LDP-NR'
        label = (f'{parent}; {method.upper()}; {graph_size} trp/graph; '
                 f'{type_label}')
        plt.plot(px, py, label=label)
        plt.legend()
        plt.show()