-
Notifications
You must be signed in to change notification settings - Fork 35
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Wrapping LanItems::Items into objects #796
Conversation
3ef571e
to
06b4a45
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For of all, thanks a lot for all this work. Reviewing your PR has helped me to learn a few things :)
However, IMHO this PR introduces too many changes, but I must admit that it might be tricky to split this into smaller (and not breaking) PRs.
For me, the most interesting part is that you have introduced a Y2Network::InterfaceConfigBuilder
class, which looks like a sort of controller. And yes, I can see the need for that. Moreover, some
methods like LanItems#needFirmwareCurrentItem
, which received no arguments in the past, now get a iface
argument. That's an improvement.
You have said that the PR will break quite some stuff at this point, and that's something we knew
that could happen. But I am not sure whether we should try an alternative approach like rewriting
the workflow for one kind of interface top-bottom (as @teclator proposed). Then, the ConfigWriter
would take care of writing changes to disk for those interfaces. And, actually, we could write new
widgets for that part (moving slowly from the tangled mess of *Dialog
methods :)).
The benefit would be to have separate and clean-code, instead of keep fighting against/patching the old one. We could take advantage of quite some work from this PR (all the reading part,
including hwinfo stuff) and we could start rewriting this part in an incremental way (and keep the whole thing working).
Well, I only wanted to open the discussion. What is your POV? @teclator @mchf @jreidinger
src/lib/y2network/interfaces.rb
Outdated
# | ||
# FIXME: Intended for LanItems::Items separation | ||
# proper cleanup is must | ||
class Interfaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of having a container for interfaces (as we have in storage-ng), although I would call it InterfacesCollection
or InterfacesList
.
However, this class is responsible for converting the information from sysconfig into Interface
objects too (it receives the result of calling Yast::LanItems.Items) which looks wrong to me. What if tomorrow we want to support any other technology? I think it should receive an array of Interface
objects.
Then, who is responsible for building the Interface
objects? I think ConfigReader::Sysconfig
(or any ancillary class) is.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
we can separate this conversion method to another kind of reader for example. I'm not against that.
Renaming from Interfaces
to e.g. InterfacesCollection
also makes sense.
# Load sysconfig into new backend | ||
# It is done here bcs this method is currently called during "yast2 lan" | ||
# initialization, so it makes configuration available for all submodules | ||
system_config = Y2Network::Config.from(:sysconfig) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why here and not in Lan.Read
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lan
module should be replaced byY2Network::Config
in the future- place it properly in
Lan::Read
is a bit tricky - I don't see any benefit from placing it into
Lan::Read
I basically share the same opinion. |
I've been checking some internals and I am not sure whether it is possible to follow the top-bottom approach we were talking about :( |
well originally I started with this to sort out my thoughts bcs I was completely unsure where should I start from when turning old internals into shining new ones. However, somewhere around 25th commit I started to be slightly more optimistic - and it doesn't happen that often to me in y2-network ;-) In the end I think that, as we are at the very beginning of 15sp2 development, we should be able to fix x86 parts easily and quickly when we go by way of accepting this patch. For example we can keep it living a side of master for while and ask QA team to run this version against current integration tests, and so on. What I'm a bit more scared of is s390, but on the other hand I don't see anything that supper complex what should break. There are some additional dialogs which need to be tested and a difference in device initialization. A part of that everything else should be the same. So again nothing what should not be fixed after one or two sprints of intensive work when we have an s390 machine for playing. What benefits I see? Better test coverable code (currently some parts are very hardly testable bcs of nasty side effects, spaghetti code, ... you now ;-) and cleaned code. For example during work on this patch I did a grep for And in the end I don't see this as a (clean) top to down rewriting. In the end I only took the So to summarize that ... I think that doing "the major" cut is best now (I mean now == before all that alfa, beta, GC phases) otherwise we can simply end with nicely rewritten UI into CWM but still working on top of sucking internals. I also don't see it doable and / or useful to rewrite it for example on per interface type basis (I mean create an object e.g. for wlan and rewrite parts of workflows related to this type into objects). |
And if you'd ask me how to continue ... Up to now I have been (manually) testing only eth workflows, so now I'd continue with other types. This should create a hints for building a Some other task which remains and should be done independently on the future of this patch is rewriting udev handling into objects. |
I am still thinking that this PR does too much :), but I can see your points and I think we could work from here. I have started a deeper review, but I would like to set a target and finish (and merges it) as soon as possible so we can start working in parallel. I want this PR to stop growing :) |
me too. Only think I plan to add is tests and necessary fixes, no more functionality. |
# @return [String] type which is intended to be build | ||
attr_accessor :type | ||
|
||
include Y2Network::InterfaceDefaults |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this in a different module? Are you going to reuse it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no. It is just bunch of methods moved from old code which I haven't check in depth yet.
builder.set(option: "ETHERDEVICE", value: Ops.get_string(@settings, "ETHERDEVICE", "")) | ||
builder.set( | ||
options: "VLANID", | ||
value: Builtins.tostring(Ops.get_integer(@settings, "VLAN_ID", 0)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would replace the Builtins#tostring
with modern Ruby code.
src/include/network/lan/address.rb
Outdated
@settings, | ||
"TUNNEL_SET_OWNER", | ||
"" | ||
elsif Builtins.contains(["tun", "tap"], builder.type) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same than above.
elsif Builtins.contains(["tun", "tap"], builder.type) | ||
builder.set( | ||
option: "TUNNEL_SET_OWNER", | ||
value: Ops.get_string(@settings, "TUNNEL_SET_OWNER", "") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same than above (dropping Ops
in this case).
src/lib/y2network/interfaces.rb
Outdated
# Converts old LanItems::Items into internal data format | ||
# | ||
# @return [Interfaces] a container with available interfaces | ||
def from_lan_items(lan_items) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's keep it by now. I can move this logic to ConfigReader::Sysconfig
(or an ancillary class).
@@ -98,9 +98,6 @@ def main | |||
@initialized = false | |||
|
|||
@backend = nil | |||
|
|||
# Y2Network::Config objects | |||
@configs = {} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure whether this is a good idea.
src/modules/LanItems.rb
Outdated
else | ||
newdev["NETMASK"] = @netmask | ||
end | ||
# newdev["IPADDR"] = @ipaddr |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why not remove the code instead of just commeting it?
@@ -2154,14 +2100,15 @@ def Commit | |||
|
|||
# ZONE uses an empty string as the default ZONE which means that is not | |||
# the same than not defining the attribute | |||
current_map = (GetCurrentMap() || {}).select { |k, v| !v.nil? && (k == "ZONE" || !v.empty?) } | |||
iface = config.interfaces.find(builder.name) | |||
current_map = (!iface.nil? && iface.configured ? iface.config : {}).select { |k, v| !v.nil? && (k == "ZONE" || !v.empty?) } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is pretty hard to read. What about splitting it in different lines?
Some relevant things to fix:
|
# | ||
# @return [Boolean] true when both collections are equal, false otherwise | ||
def ==(other) | ||
@interfaces - other.interfaces && other.interfaces == @interfaces |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, but I do not get the conditions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep, probably something like other.interfaces.sort == @interfaces.sort
9337878
to
033546d
Compare
@@ -1005,12 +960,12 @@ def AddressDialog | |||
] | |||
) | |||
|
|||
wd["IPOIB_MODE"] = ipoib_mode_widget if LanItems.GetCurrentType == "ib" | |||
wd["IPOIB_MODE"] = ipoib_mode_widget if builder.type == "ib" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this start smelling to me. This type should be enum or own class. But something for future. As plain text comparison can be silently failing for long type. ( I made types often ).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
definitely. But we don't have type hierarchy yet. Just one simple Interface class.
LanItems.firewall_zone = firewall_zone.store_permanent if firewalld.installed? | ||
builder.set(option: "STARTMODE", value: Ops.get_string(@settings, "STARTMODE", "")) | ||
builder.set(option: "MTU", value: Ops.get_string(@settings, "MTU", "")) | ||
builder.set(option: "ZONE", value: firewall_zone.store_permanent) if firewalld.installed? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, I am still not much fan of this set which is just hidden builder["STARTMODE"] = Ops.get_string(@settings, "STARTMODE", "")
so basically hash like API. I still prefer something like builder.startmode = Ops.get_string(@settings, "STARTMODE", "")
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
no problem with that, already done something similar for the Interface and HwInfo classes and make sense even here. I just wanted isolate all cases where the builder should be used instead of (e.g.) direct access to LanItems internals. For that purpose was hashlike api enough
LanItems.vlan_id = Builtins.tostring( | ||
Ops.get_integer(@settings, "VLAN_ID", 0) | ||
if builder.type == "vlan" | ||
builder.set(option: "ETHERDEVICE", value: Ops.get_string(@settings, "ETHERDEVICE", "")) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we plan to keep this @settings
if so, then I would prefer something like builder.load_settings(@settings)
which will do this transformation inside.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I currently don't see any benefit in keeping it.
"STARTMODE" => LanItems.startmode, | ||
"IFPLUGD_PRIORITY" => LanItems.ifplugd_priority, | ||
def initialize_address_settings(builder) | ||
@settings.replace( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
so in future we will pass builder instead of @settings to CWM widgets, right?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to. Original idea was to keep "open" interface in it and either submit or drop it as the user wishes (confirms Add / Edit or cancel it)
@builder.name = iface.name | ||
@builder.type = iface.type | ||
if iface.configured | ||
@builder.load_sysconfig(iface.config) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
again too much tight to sysconfig. I prefer here generic load. In general I do not like naming here, as it is more like load current config to builder. Not anything about sysconfig.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes it is. Bcs I'm currently injecting new things into old workflow. And as i currently use sysconfig option names as identifiers in the builder I've picked this name. Definitely something for improving
@builder.type = iface.type | ||
if iface.configured | ||
@builder.load_sysconfig(iface.config) | ||
@builder.load_s390_config(LanItems.s390_ReadQethConfig(iface.hardware.name)) if iface.hardware |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope that it should be hidden in that generic load.not job of UI to handle different internals for different arch. What is its job if there is specific option for s390, then we need to have specialized UI, but not for loading on s390.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whole Add / Edit / ... workflow handling needs some polishing as it is somehow spread over the code. We currently need to isolate what is needed to keep ... so thanks for any hint ;-)
@builder.load_sysconfig(iface.config) | ||
@builder.load_s390_config(LanItems.s390_ReadQethConfig(iface.hardware.name)) if iface.hardware | ||
# FIXME: move to more appropriate place | ||
LanItems.operation = :edit |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hope config itself can recognize if it already existed before or not. Maybe something trivial like system_name ( so name before all renames? ) and if it is nil, then it means new and of course nice method for it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes ... already planed and described somewhere in this PR. This operation can be identified according to configuration name (I've introduced interface.name to be "persistent" and reflects current state of the system) name exists -> edit action, name do not exist -> add action
@@ -566,6 +628,8 @@ def MainDialog(init_tab) | |||
break if input_done?(ret) | |||
end | |||
|
|||
@builder = nil |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
is it needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
most probably not. Just wanted to catch some unexpected accesses.
"overview" => -> { MainDialog("overview", builder: iface_builder) }, | ||
"add" => [-> { NetworkCardSequence("add", builder: iface_builder) }, true], | ||
"edit" => [-> { NetworkCardSequence("edit", builder: iface_builder) }, true], | ||
"init_s390" => [-> { NetworkCardSequence("init_s390", builder: iface_builder) }, true] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sharing builder accross sequence looks reasonable approach for me.
{ name: "wl_channels", default: nil }, | ||
{ name: "wl_bitrates", default: nil } | ||
].each do |hwinfo_item| | ||
define_method hwinfo_item[:name].downcase do |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why downcase when everything is upcase? or there is keys in hwinfo with upper case? Because this can produce strange looking result like DevName
-> devname
where you expect dev_name
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
only readability ... i prefer obect.method
over object.METHOD
or so. And I'm also not sure if there is any exception in naming in the hash provided by libhd
alias_method :name, :dev_name | ||
|
||
def exists? | ||
!@hwinfo.nil? |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
to be honest I do not like much this approach with checking everywhere nil. What about raising exception when hwinfo not found and simply return constant with defaults that is defined above? And does it make sense at all having this hwinfo full of nils, false and empty arrays?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
i planned to keep only necessary things or load on demand in the future.
Co-Authored-By: imobachgs <igonzalezsosa@suse.com>
Co-Authored-By: Knut Alejandro Anderssen González <kanderssen@suse.de>
8597b47
to
e0b04f1
Compare
src/lib/y2network/hwinfo.rb~HEAD
Outdated
@@ -0,0 +1,30 @@ | |||
# Copyright (c) [2019] SUSE LLC | |||
# |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This file is a mistake
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
true
* Add an InterfacesCollection class * Taken from #796. * Replace Array<Interface> with InterfaceCollection objects
Some parts were already merged in other PRs, remaining parts were postponed for now. |
Currently you can somehow work with "Add" and "Edit" interface workflows. It is only partially working in sense that it do not crash and handles configuration / user input somehow. Definitely needs some more love but should contain all crucial updates which should be needed for switching to new backend
Follow up of #782