Giter VIP home page Giter VIP logo

stratisd's Introduction

stratisd

A daemon that manages a pool of block devices to create flexible filesystems.

Background

Stratis (which includes stratisd as well as stratis-cli), provides ZFS/Btrfs-style features by integrating layers of existing technology: Linux's devicemapper subsystem, and the XFS filesystem. stratisd manages collections of block devices, and exports a D-Bus API. Stratis-cli's stratis provides a command-line tool which itself uses the D-Bus API to communicate with stratisd.

Website

See https://stratis-storage.github.io/.

Getting involved

Communication channels

If you have questions, please don't hesitate to ask them, either on the mailing list or IRC.

Mailing list

Development mailing list: [email protected], -- subscribe here.

IRC

irc.libera.chat #stratis-storage.

For Developers

Stratisd is written in Rust, which helps the implementation be small, correct, and avoid requiring shipping with a large language runtime.

Issue tracking and Development

Stratisd development uses GitHub issue tracking, and new development occurs via GitHub pull requests (PRs). Patches or bug reports may also be sent to the mailing list, if preferred.

Setting up for development

Development Toolchain

stratisd can be built using a range of Rust toolchain versions. The recommended development toolchain usually tracks the Rust stable version, although it may lag it slightly. stratisd is guaranteed to be able to be built on its lowest supported toolchain, which may lag the recommended development toolchain by a few minor versions.

Contributors should use the recommended development toolchain if possible, since the CI makes use of the Rust linter and formatter for that version. The recommended development toolchain version can be determined by inspecting the CI configuration files for the project.

Building

Stratisd requires Rust and Cargo to build. These may be available via your distribution's package manager. If not, Rustup is available to install and update the Rust toolchain. Once toolchain and other dependencies are in place, run make build to build, and then run the stratisd executable as root.

Building tests

The Makefile provides a target, build-tests which allows compiling the tests without running any of them, as a convenience to developers.

Secondary dependencies

The Stratis ci repo includes a script, dependencies_fedora.sh, which installs all the development dependencies for stratisd and its CLI on Fedora.

Formatting

Stratisd makes use of rustfmt to enforce consistent formatting in Rust files. PRs must pass the fmt task in the CI in order to be merged. Run make fmt to ensure your changes conform to the expected formatting before submitting a pull request. Formatting changes a bit with different versions of the compiler; make sure to use the current development version.

Linting

Stratisd makes use of clippy to detect Rust lints. PRs must pass the clippy task in the CI in order to be merged. To check for lints, run make clippy. The lints change a bit with different versions of the compiler; make sure to use the current development version.

Configuring

Stratisd runs as root, and requires access to the D-Bus system bus. Thus in order to work properly, a D-Bus conf file must exist to grant access, either installed by distribution packaging; or manually, by copying stratisd.conf to /etc/dbus-1/system.d/.

Setting Log Levels

The command-line option, --log-level, may be used to set the stratisd log level. This option sets the level for the stratisd components only.

For finer-grained control over the log level of any stratisd component or dependency use the RUST_LOG environment variable. Please consult the documentation for the env_logger crate for additional information on the use of RUST_LOG.

Testing

Stratisd is tested in two ways. The first way makes use of the Rust test infrastructure and has more access to stratisd internals. The second way makes use of the stratisd D-Bus interface.

Tests that make use of the Rust test infrastructure

Stratisd incorporates two testing modalities:

  • safe unit tests, which can be run without affecting your storage configuration
  • unsafe unit tests, which may create and destroy devices during execution

To run the safe unit tests:

$ make test

For a description of the unsafe unit tests, necessary setup steps, and how to run them, see README_tests.md.

Test that interact with stratisd via the D-Bus

For a description of the D-Bus-based tests see tests/client-dbus/README.rst.

Allowed Bugs

stratisd has some bugs; most of these we intend to address in due course.

There is one bug that we have chosen not to fix. This is a bug in our D-Bus layer that will allow incorrect un-marshalling of certain D-Bus values if a D-Bus method is invoked with arguments that do not conform to the expected signature of the method. See the GitHub issue for additional details about this bug. Behavior of stratisd is undefined if a method is called under the particular circumstances that allow the bug to manifest.

Licensing

MPL 2.0. All contributions retain ownership by their original author, but must also be licensed under the MPL 2.0 to be merged.

stratisd's People

Contributors

agrover avatar ambaxter avatar bgurney-rh avatar bmr-cymru avatar cgwalters avatar cuviper avatar drckeefe avatar guillaumegomez avatar ignatenkobrain avatar jbaublitz avatar jcastill avatar jelly avatar kianmeng avatar kintaro avatar leseb avatar lleshchi avatar martinpitt avatar mati865 avatar mberndt123 avatar mergify[bot] avatar mulkieran avatar mvollmer avatar nak3 avatar nickcao avatar pyup-bot avatar tasleson avatar trgill avatar waltdisgrace avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

stratisd's Issues

unwrapping contents of in message and wrapping contents of out message

This is something we should figure out how to do generically.

Probably the way it is done in the CLI is a good way, i.e.,

  • Parse the signature.
  • Generate functions from the signature that build the desired value from the corresponding value in the other types. in args need to be broken down from MessageItems to their inner types, outargs need to be built up.

I solved this problem with decorators in the CLI. This is nice, because the majority of the cost, that is, the generation of the functions, gets paid when the module is loaded. Only the cost of executing the functions gets paid at runtime. How to pull of the same thing in Rust is not so obvious. In the CLI, it is not necessary to unpack the in arguments, since the types of the in arguments are subtypes of the desired arguments due to the implementation of the Python library.

Should SimEngine use blockdevs?

blockdev::BlockDevs is actually going to want to write to block devices. We're going to be adapting it for use it in StratisEngine. Does SimEngine really want to need & access actual blockdevs, or should it "sim" them?

Many fewer methods should return StratisResult type.

At least, we need to separate the external view by clients of the dbus API from the internal view of the engine. Right now StratisResult is for both.

Probably, it would make sense to make a new return type for the engine, something like EngineResult, which will have its own internal error type as well.

Message loop needs to not live in dbus_api.rs

It needs to be in main(), and then if the dbus socket becomes readable, then we need to call our dbus code to handle the incoming request.

This is because we will also be expecting non-DBus events, such as timers, netlink events, and who knows what else.

I'm going to set a not-very close milestone for this because I do know we're only worried about DBus near-term, but this will need to happen at some point.

Consider strategy for prompting user for comfirmation on certain actions

The idea is that there are some destructive actions, which the user would be grateful to be warned about? This is probably easily done by wrapping "action" methods, like:

def prompt_wrapper(the_func):
    if the_func in DESTRUCTIVE_ACTIONS and SAFE_MODE:
        def new_func(namespace):
            # do the prompt, then
            the_func()
    else:
        new_func = the_func

    return the_func

Or the wrapper could be made more specific to individual action methods in a number of ways.

Put dbus stuff in its own subdirectory before it gets big.

And export constants and so forth as appropriate.

I think we should do this sooner rather than later.

I think that the contents of dbus_api.rs could be split into multiple files, for manager, for pool, and so on and that in this way any problems with circular dependencies can be avoided.

We need a README.md

We need a README.md so that potential developers visiting the repo on GitHub will get information on what Stratis is supposed to do and how to contribute code or test it.

CreateVolumes should return a message if there is a volume name collision

Currently, there is just no reply:

[mulhern@dhcp-25-209 stratis-cli]$ PYTHONPATH=./src ./bin/stratis logical create deadpool oubliette
Namespace(func=<function create_volumes at 0x7f10beeae410>, pool='deadpool', subparser_name='create', volume=['oubliette'])


[mulhern@dhcp-25-209 stratis-cli]$ PYTHONPATH=./src ./bin/stratis logical create deadpool oubliette
Namespace(func=<function create_volumes at 0x7fcb09f2d410>, pool='deadpool', subparser_name='create', volume=['oubliette'])


Traceback (most recent call last):
  File "./bin/stratis", line 39, in <module>
    main()
  File "./bin/stratis", line 36, in main
    return next(execution)
  File "/home/mulhern/my-projects/stratis-cli/src/stratis_cli/_main.py", line 41, in run
    raise err
dbus.exceptions.DBusException: org.freedesktop.DBus.Error.NoReply: Message recipient disconnected from message bus without replying

This is due to a seg fault, rather than normal execution. It fails to return a message because it crashes.

Initial implementation of SimEngine

SimEngine should:

  • Fully support methods in Engine and Pool traits
  • Support basic adding/removing of simulated pools, filesystems, and blockdevs, to the extent possible via the DBus API
  • Optionally support returning random error results instead of always succeeding

Use clippy

https://github.com/Manishearth/rust-clippy

Clippy is a Rust lint that is pretty good at catching style issues or non-idiomatic code. Unfortunately it requires Rust nightly to run as a compiler plugin, but it can also be used as a Cargo subcommand. However, I found some of its lints were a little too picky, but these can be disabled on a per-lint basis.

Need to think about the semantics of creating a cache automatically when pool is created

Currently, a cache is created automatically when a pool is created.

This is tricky, because it is not clear what devices should belong to the cache.

Currenlty a cache has only properties, not methods. One of a cache's properties is its Dev. In the instance I'm looking at, I created a pool consisting of a single dev. The cache's unique dev is thus the pool's unique dev. This seems like it must be wrong.

If the cache is automatically created when the pool is created, initially, the cache will have no devices, or else it must get devices from the devices in the pool. It is not clear to me how stratis will always choose the correct devices for the cache, so the second option seems bad. This leaves the first option, with a cache with no devices. Given this possibility, stratis will have to be able to deal with the possibility of an empty cache or a cache that is way too small, such that there is no benefit.

The alternative is to make the user do the work, creating the cache and specifying the devices for the cache explicitly. This feels more desirable to me than auto-creating the cache. Since stratis presents a real API a smart client of stratis could encapsulate the creation of a pool and its cache into s single action in a reasonable way, i.e., stratis doesn't have to do so itself. This is the way I would prefer.

Need to know the proper idiom for handling a list, any one of which may have a problem

I introduced a bug in dbus_api.create_pool() while trying to handle the possibility that the items in the array of device specs might have the incorrect type. Here's the code:

    let mut devstrings = devs.iter().map(|x| x.inner::<&String>());
    if devstrings.any(|x| x.is_err()) {
        return Err(MethodErr::invalid_arg(&item1));
    }
    let blockdevs = devstrings.map(|x| Path::new(x.unwrap())).collect::<Vec<&Path>>();

The problem is that devstrings, which is a Map, is mutable. If I check if any of the results matches Err, I consume the iterator. So, if all the inhabitants of the array have the correct type, there is a bug, because blockdevs is always the empty vec. In other words, the code has to hit the return statement, or the list of devices has to have been empty originally, for this code to be correct.

I've thought of a lot of ways to solve this problem, involving use of various iterators. But it is a very generic problem, it will recur, I'ld prefer to adhere to the accepted solution if there is one.

A description of the problem:

  1. For each element in the vector, perform an operation that may fail, and which produces a new element. This is a basic map operation, except for the failure part.
  2. If there is a failure for any element, exit from the method with an Err value.
  3. Otherwise get a new iterator which contains the elements on which the operation is performed.
  4. Do something with that new iterator.

Handle DM events without polling

See https://bugzilla.redhat.com/show_bug.cgi?id=817854

Devicemapper's mechanism for receiving events currently requires a thread to sleep in an ioctl call for each device in question. I'm working with the LVM team to add a new mechanism that will achieve this goal in a non-blocking, single-threaded fashion. Alasdair and I have a preliminary agreement on how to proceed, so I need to:

  1. Write a doc describing the proposed solution and its implementation details
  2. Get initial internal signoff from lvm-team
  3. Write the code
  4. Submit upstream, get it merged
  5. Wait until a kernel is released that includes the new feature
  6. Implement support in devicemapper-rs
  7. Implement support in stratisd event loop

Until this happens, stratisd may need to poll -- read status from dm devices periodically to make sure everything is fine.

Design: Implement Engine errors as DBus MethodErrs

Right now a DBus client can get errors in two ways from calling our API.

First, if the call was malformed, for a nonexistent object, or didn't have enough parameters, the client will get a standard DBus error. Second, if the call was good but Stratisd internally had a problem, the result is a successful DBus call, but with an error type in the result other than STRATIS_OK.

I propose we make Stratisd errors actually be returned as DBus errors. We can define own own error names that correspond our existing values in dbus_consts.rs StratisErrorEnum.

This will let clients still differentiate between error types if desired, or if they just want to know success/fail, they can determine this without doing a secondary check for more errors once they know the method call returned success.

thoughts @trgill @mulkieran @tasleson ?

Should still consider if CreateSnapshot should specify multiple names for the snapshots

There was the idea of a 1 to many relationship broached at one point. Could we do something smart for this relationship. And, what if the user wants to specify a whole lot of snapshots? Should we actually force them to specify the names of all the snapshots? Do they not care about the names and would they like to specify snapshots in differrent ways?

How should dbus API's createpool() method get the object path of new pool?

Previously the engine returned the object path. It looks to me like that now the dbus API has the job of synthesizing the object path. But, in that case, the engine's createpool method must still return something, and it is currently defined to return nothing. Should it be returning a pool object? Should it be returning a pool name? DBus API must maintain a mapping between object paths and some identifier for pools, so this is an important question.

Initial implementation of the DBus API

Initial implementation should allow pools, filesystems, and blockdevs to be added and removed. This includes:

  • Validating method arguments are present and typechecked
  • Calling the appropriate Engine or Pool method
  • Converting the response, either success or error, to the method's defined return values

Need to think about rolling back on failures

An example is creating a list of volumes. Currently, the simulator creates the volumes it can, and if it fails to create any volume, signals a STRATIS_LIST_FAILURE meaning that it can not be more specific than that, because it may have many failures, with many causes. It is possible to look through the results for the individual volumes to find out details.

Should it roll back, returning the reasons for its failure as before, but not actually creating any volumes?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.