- want new libstorage - can't simply get a drop-in replacement - have to handle target map somehow - want a wrapper for the target map - replace the interfaces for that wrapper when new libstorage is ready
As most of you probably already know, we want a new, much improved libstorage.
Not only will this clean up about 15 years worth of legacy (a.k.a. cruft), it will also remove quite a number of limitations of the existing code and architecture. Many of those limitations are blockers for features (or for fixing bugs) that get requested again and again. We have a long, ever-growing list of such features and bugs.
But getting a new libstorage is only half of the story (or even less); the rest is about using it in all the existing code like in yast-storage (storage proposal for the installation; expert partitioner) or yast-bootloader.
Most of that code also got auto-converted from YCP to Ruby which did not exactly make it any prettier; that's natural for such an automated conversion, but still we should start turning it into real Ruby code that meets our criteria for code quality at some distant time in the future.
We don't want source files with 7000 lines of code any more. We don't want functions with 500+ lines. We don't want it to rely heavily on (file-) global variables. We don't want monstrosities like that "target map" being accessed and modified all over the place.
Personally, I would very much like to refactor the storage proposal stuff; I'd like to have clean, nice Ruby classes that can be properly unit-tested (without formatting my hard disk) and that are well-documented.
Arvin already made a new libstorage implementation during one of the hackweeks (last year?) that looks very promising, but which is still incomplete. We don't want to let bit-rot take over until this becomes a total wreck. We already have a number of backports for that.
Right now, getting to those goals is very difficult; we can't just put a stop to all features for upcoming releases, much less L3 calls or high-priority bugs. So we can't just implement the new libstorage completely, create the proper unit tests, and convert all of the existing storage-related Ruby code to use it; this might easily take half a year or more.
This is getting more complicated due to the way libstorage is used in the existing storage-related Ruby code (inherited by the old YCP code): It heavily relies on a data structure called "target map" which is a deeply nested data structure to represent disks, partitions, RAIDs, volume managers and whatnot. This target map is used all over the place, it (or parts of it) is handed over from one function to the next in all that code, parts are extracted, deleted, added, modified, uglified, whatever. That target map is the pivotal part of all that code.
This is bad because it's not that obvious when, how and why it is used everywhere. But it's also good because it provides us with an abstraction layer between libstorage and the Ruby world.
So, to get anywhere at all in the forseeable future, I propose to start refactoring at that place: I'd like to have a Ruby class that wraps that target map. For starters, I'd simply put the existing map into it (in the constructor and in a setter method) and get it out again, then begin to add methods to access all the individual parts of it - real methods, not just accessing map keys and values. And use those methods in the appropriate places, of course.
When some time in the (hopefully not so far away) future the new libstorage gets ready, we could simply change that wrapper to use methods from that new libstorage. This should be transparent to the other code.
Yes, I know, this approach is not as clean as a complete rewrite. We might end up with some other legacy code in the process (but if we do it carefully, we should be able to minimize that). It will mean additional work that would not be necessary with a complete rewrite.
But then, I believe it is unrealistic to ever do a complete rewrite and put all other development or maintenance in the storage area to a complete halt. If we ever want to get anywhere, we will have to take this approach.
How do you get over a mountain? Not by whishing it wasn't there at all. No, you take one step. Then you take another. And another. And another. And at some time, you will find the mountain is behind you.
This should be the first step crossing that mountain.
Thoughts? Ideas? Bitching? Rants?
thanks for bringing this up.
The main problem I see with your approach is that we end up with that wrapper at the end of the day. So we would have libstorage <-> wrapper <-> ruby. Depending on the complexety of the wrapper it means more documentation, more difficult debugging and more work for new features.
I my proof of concept redesign there is also a wrapper but at a different level: It provides the C++ interface of the existing libstorage for the redesign libstorage. That way the ruby code can stay as it is while switching the underlying library and then the ruby code can be converted piece by piece. In the end the wrapper (and the target map) will be obsolete.
In the proof of concept I have already show that such a wrapper is possible (in Febuary it was able to install the most simple system) and that the interface of the new libstorage can be used at the same time from ruby. I admit that since the data structure of the redesign libstorage is very different from the old one the interface wrapper is not simple but tricky and implementing every corner-case will be difficult. So including the wrapper in a maintained release it not what I recommend.