-
Notifications
You must be signed in to change notification settings - Fork 888
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
add a dispatch to Ephemeral net class for is_BSD() #363
Conversation
first test: cloud-init once again doesn't recognize my DataSource second test: "it works", here's the generated rc.conf: clear_tmp_enable=YES
syslogd_flags=-ss
sendmail_enable=NONE
hostname=cloud-init-dev01
ifconfig_vtnet0=DHCP
ifconfig_vtnet0_ipv6='inet6 2a01:4f9:c010:6cdf::/64'
sshd_enable=YES
ntpd_enable=YES
# Set dumpdev to "AUTO" to enable crash dumps, "NO" to disable
dumpdev=AUTO
zfs_enable=YES
cloudinit_enable=YES
ifconfig_vtnet0_name=eth0
ifconfig_eth0=DHCP which of course then means that IPv6 no longer works, because vtnet0 has been renamed to eth0, and the freebsd renderer doesn't support IPv6 yet. |
961d110
to
24ab08c
Compare
@goneri would you mind taking a look at this? |
Hi! Well, I can try to run my usual tests (ConfigDrive and OpenStack+DHCP). At least we will know if there is a regression. |
Hey Mina, thanks for putting this together! I've only taken a cursory look at this, but I'm worried about using a pattern that ensures we will have to have many, many different functions within a same class to support multiple platforms. This seems like a clear case where separate classes for each of BSD and Linux would make sense. One approach to implementing such classes piece-by-piece (rather than landing a single enormous refactor PR which would take ~months to write and review) would be to have Another, perhaps better long-term approach, would be to follow @rjschwei's proposal on the mailing list from back in January and start moving these methods onto the I think the galaxy brain iteration of this approach would actually move (As an aside, we may want to consider implementing the distro networking so it's in a separate namespace because e.g. |
my understanding of a can a method of subclass still be extended? class Linux(Distro): # i know this is a lie, let's just pretend
def __init(self):
self.networking = Networking.new()
class Networking:
def is_wireless(self):
pass how do we extend |
I would envision something like this: diff --git a/cloudinit/distros/__init__.py b/cloudinit/distros/__init__.py
index e99529dff..a71bf7ede 100755
--- a/cloudinit/distros/__init__.py
+++ b/cloudinit/distros/__init__.py
@@ -56,6 +56,21 @@ PREFERRED_NTP_CLIENTS = ['chrony', 'systemd-timesyncd', 'ntp', 'ntpdate']
LDH_ASCII_CHARS = string.ascii_letters + string.digits + "-"
+class Networking(metaclass=abc.ABCMeta):
+
+ @abc.abstractmethod
+ def is_wireless(self):
+ pass
+
+
+class LinuxNetworking(Networking):
+
+ def is_wireless(self):
+ # use common Linux commands to check
+ is_wireless = util.subp(['some', 'helper'])
+ return is_wireless
+
+
class Distro(metaclass=abc.ABCMeta):
usr_lib_exec = "/usr/lib"
@@ -66,11 +81,13 @@ class Distro(metaclass=abc.ABCMeta):
init_cmd = ['service'] # systemctl, service etc
renderer_configs = {}
_preferred_ntp_clients = None
+ networking_cls = LinuxNetworking
def __init__(self, name, cfg, paths):
self._paths = paths
self._cfg = cfg
self.name = name
+ self.networking = self.networking_cls()
@abc.abstractmethod
def install_packages(self, pkglist):
diff --git a/cloudinit/distros/bsd.py b/cloudinit/distros/bsd.py
index 37cf93bfb..9173db75c 100644
--- a/cloudinit/distros/bsd.py
+++ b/cloudinit/distros/bsd.py
@@ -10,7 +10,15 @@ from cloudinit import util
LOG = logging.getLogger(__name__)
+class BSDNetworking(distros.Networking):
+
+ def is_wireless(self):
+ is_wireless = util.subp(['bsd', 'helper'])
+ return is_wireless
+
+
class BSD(distros.Distro):
+ networking_cls = BSDNetworking
hostname_conf_fn = '/etc/rc.conf'
rc_conf_fn = "/etc/rc.conf"
diff --git a/cloudinit/distros/opensuse.py b/cloudinit/distros/opensuse.py
index 68028d20c..19883a67d 100644
--- a/cloudinit/distros/opensuse.py
+++ b/cloudinit/distros/opensuse.py
@@ -22,7 +22,15 @@ from cloudinit.settings import PER_INSTANCE
LOG = logging.getLogger(__name__)
+class OpenSuseNetworking(distros.LinuxNetworking):
+
+ def is_wireless(self):
+ is_wireless = util.subp(['suse', 'specific', 'helper'])
+ return is_wireless
+
+
class Distro(distros.Distro):
+ networking_cls = OpenSuseNetworking
clock_conf_fn = '/etc/sysconfig/clock'
hostname_conf_fn = '/etc/HOSTNAME'
init_cmd = ['service'] So we have a parallel hierarchy of Networking sub-classes, distros specify which one they should use, and the common |
24ab08c
to
b827075
Compare
thank you for your guidance @OddBloke. what i'm still unclear on is what to do with the current module functions (such as the and by all of them i mean:
etc, etc… |
and finally, if we're splitting out utility functions from EphemeralIPv4, we need a channel to communicate back how to undo what the functions did, or else have the equivalent |
Right, this is not a small change that we're talking about. All of these call sites will need to have access to the Where we're calling these things from a datasource, we know we have For the non-datasource cases, it's less clear-cut. But, for example, the An important point I want to make (or perhaps restate): this is a big change we're talking about, but we don't need to land this change in one piece. We can (and absolutely should, IMO) migrate function-by-function, and make this big change as a series of incremental small changes. (We should figure out upfront what all the functions we intend to move are, so that we know when we are Done (TM), but we can land the individual migrations separately.)
This one isn't distro-specific, I don't think, it's related to network configuration that is passed to cloud-init, not network configuration that cloud-init is writing to the system. This is an example of something that this proposed refactor would help with: distinguishing which networking functions are to do with cloud-init's network configuration (these functions are not distro-specific, and would presumably stay in |
So I was thinking that the interface for with self.distro.networking.EphemeralIPv4Network(
nic, "169.254.0.1", 16, "169.254.255.255"
):
md = hc_helper.read_metadata(
self.metadata_address, timeout=self.timeout,
sec_between=self.wait_retry, retries=self.retries)
ud = hc_helper.read_userdata(
self.userdata_address, timeout=self.timeout,
sec_between=self.wait_retry, retries=self.retries) So we don't need to communicate anything back to the caller of The follow-up question is then: what does that implementation look like on the class EphemeralIPv4Network(metaclass=abc.ABCMeta):
def __init__(self, interface, ip, prefix_or_mask, broadcast, router=None,
connectivity_url=None, static_routes=None):
# ... the current content of EphemeralIPv4Network.__init__ ...
pass
def __enter__(self):
# ... the current content of EphemeralIPv4Network.__enter__ ...
pass
def __exit__(self, excp_type, excp_value, excp_traceback):
# ... the current content of EphemeralIPv4Network.__exit__ ...
pass
@abc.abstractmethod
def _delete_address(self, address, prefix):
pass
@abc.abstractmethod
def _bringup_device(self):
pass
@abc.abstractmethod
def _bringup_static_routes(self):
pass
@abc.abstractmethod
def _bringup_router(self):
pass Then the Linux subclass would basically be the current class (with the
This becomes a problem if |
Thanks for working through this topic. In general I'm happy to see us push toward a distro owned Networking subclass; this has a number of benefits including easier handling of rendering for the variants (knowing it's on Fedora vs. Centos vs SLES or OpenSuSE) and utlizing distro specific tooling and paths for acquiring the requested change or information. Dan has outlined a solid approach to refactoring this and I'm on board with enumerating all of the caller's users so we can see the scope of the work. A couple of thoughts for consideration.
and we've "lost" the distro variant info; and I think we want the renderer to take the distro class object; it can then abstractly call paths and networking sublcass functions to render out the appropriate content for the distro. This is not required initially, but as we work through the conversion I think it will clean up a lot of the code in say, sysconfig, which is already rather split-brained for different variants.
|
b827075
to
25f27b7
Compare
@raharper thank you very much for weighing in. however, this is a major transformation, and I will need engineering guidance, and as such invite you or @OddBloke or whoever else feels qualified (by the virtue of having the skill, insight into the code, or simply: the time) to help me out here by pushing commits providing the basic structure and tests transformation, and so i can "colour in the blanks". |
start adding FreeBSD support to EphemeralIPv4Network. Very crudely.
so we use util.is_BSD() as dispatcher, and name our functions _on_bsd() while we're at it, we can also start fixing pylint and pycodestyle errors
25f27b7
to
5ac9eb3
Compare
(FYI, I hope to put up a WIP proposal PR for that initial "start" of the refactor tomorrow, unless someone raises an objection before I get to it. So if you have a fundamental objection, please at least ping me!) |
Agreed, that sounds like the right path.
Agreed.
Well, I don't know. For a start, we can look at cloudinit code itself to see
Right
I like this quite a lot Dan. Thanks for moving this forward! |
On Thu, May 21, 2020 at 03:47:25PM -0700, Ryan Harper wrote:
> > 1. Do we expect to want to retain any cloudinit.net. wrappers for
> > out-of-tree users. There are some known users of the cloudinit
> > module API.
>
> Who are they, and do we know how badly affected they would be by an
> API break?
Well, I don't know. For a start, we can look at cloudinit code itself
to see the callers of cloudinit.net ... I look at those as "external
callers" even if they are part of the code base. The common methods
used would be a good guess toward what outside tree code might call.
If we don't have any concrete external callers then I think it's
sufficient for us to give folks a clear migration path (which in the
proposed solution would be a helper function that looks up Distro etc.
for you, and is the reason we're going to replicate all the methods
immediately in the new hierarchy). I don't think it's worth the
maintenance burden of keeping the module-level API without any known
consumers. Does that sound reasonable?
> > 1. Another path is to retain cloudinit.net as an API that is just
> > abstract methods that are provided by an instance of
> > distro.networking subclass; if at import time of cloudinit.net we
> > could determine the current distro, we could plug in the subclass;
> > avoid re-writing all callers of the cloudinit.net API from having
> > to provide/get the distro class and calling distro.net.foo whey
> > they already call net.foo.
>
> Again, I have concerns about doing work at import time. That aside,
> I see no reason why we would change the names during the refactor
> (absent some specific reason for individual functions, perhaps), so
> if we provide an interface that returns an instantiated Networking
> object, then `net = please_give_me_a_networking_object()` could be a
> fairly decent replacement for `from cloudinit import net`; both
> return a namespace that contains networking-related callables with
> the same names and similar interfaces.
>
> If we do want to provide a consistent API for external consumers,
> however, that does mean that we need to slightly revisit how we
> approach this refactor. If, as previously proposed, we did it by
> removing a function from `cloudinit.net` and adding it to the
> appropriate `Networking` (sub)classes, one at a time, then we
> wouldn't have a complete API in either location from the time the
> first function is moved to the time the last function is moved. That
> is going to be longer than this API can acceptably be broken for,
> because of the upstream snapshotting we do for Ubuntu.
>
> So, instead, I propose that at the start of this effort, we
> determine what functions from `cloudinit.net` should be moved over
> (as we planned to do anyway) and then add concrete method
> implementations for every single one of them to the `Networking`
> super-class that look like this: <snip>
I like this quite a lot Dan. Thanks for moving this forward!
Great! This is what I'll attempt an initial implementation of, for
further kicking around.
|
add BSD support to EphemeralIPv4Network by dispatching on
is_BSD()