Join GitHub today
GitHub is home to over 31 million developers working together to host and review code, manage projects, and build software together.Sign up
Add new module lxd_network #31428
This PR add a new module to manage lxd networks, a recent feature.
The APIs are described at the following urls :
The whole purpose of this module is to allow the management of networks configuration inside recent lxd versions with ansible.
First of all, thank you very much for your pull request!
I tried the lxd_network module on my Ubuntu 16.04 and LXD 2.18.
However there are some points to be improved.
(1) When I ran the playbook below to create network and ran it again, I expect the task state to be
(2) When I ran the playbook below to rename the network created above, the network of the old name still existed with the
My ansible environment:
In the pull request Add lxd_container module by hnakamur · Pull Request #2208 · ansible/ansible-modules-extras, I decided to use the LXD API instead of the
However after that, I noticed the feature to manage remotes is missing in the LXD API.
Maybe it is better for
Whichever, we'd better to select features of
@Nani-o What do you think?
Thanks @hnakamur taking the time to review this.
(1) I will look into implementing a check to maintain idempotency
(2) That's a bug with lxd that I have already reported and had been fixed see
I'm not against the idea of using lxd cli client, because as we already talked about it allows for patterns like :
Where remote is a remote host configured in the lxd client :
For this to work, the configuration has to be changed on the lxd-server1 host :
So we could imagine a remote argument that would allow this :
In this example it means that localhost has an lxd-client configured with the correct remote, and we should then be able to create networks/containers/configs/... from that host.
The only two concerns I have are :
If it meets the standard of ansible, I would be happy to apply thoses changes to my pull request.
I have written a first version of the module using lxc command, and I feel that it's gonna be complicated.
The first problem I encountered is to modify the configuration of the network.
When you create a network, you can specify inline values :
Thoses values are specific to the "config" part, you can't specify the description this way for example. So you need a second command to edit the description.
When you then want to edit theses values, for example when the network is already present, but the config is different. There is 2 solutions :
The second solution would mean executing multiple set and unset command in order to remove/replace the old_keys by the new_keys.
While the first solution could potentially work, it is difficult to maintain idempotency, for example if you only specify an ipv4.address, the "lxc network create" command still add some defaults values :
In this case a second run based on the config key would result in a difference, meaning two runs that changed things.
The only acceptable solution I could imagine, would have been to create the networks with no configs and then pushing it using stdin and "lxc network edit" command. But you can't create an "empty" network unlike profile :
I'm not sure anymore that using the cli is a good idea, even if the API does'nt have all the feature, it is a reliable way to communicate with LXD.
And I just tested against your point (1) :
It's actually not a behavior of the lxd cli client, but the API add ipv4 and ipv6 defaults when you don't specify them. I gonna do some tests and think about a good implementation.
For point (2), thanks for your investigating time to implement the client using the lxc command.
I agree with you. We'd better prefer using the LXD API here.
I added the modifications to maintain idempotency. In order to do that, if either one of the subkeys ipv4.address or ipv6.address in the config argument aren't specified, we assign a value of none.
Since they are subkeys of the config argument, I couldn't declare the default value of the argument the "ansible way" :
The default value will only be used when config isn't supplied, but we need to enforce subkeys of the config argument, not the config argument itself. So I instead merge a dictionnary that contain the default value with the config supplied by the user.
There is certainly a better way to do that, but i'm not seeing how right now, and I don't know if it's a deal breaker for this module. I've added some doc to explain the behavior.
EDIT : Perhaps this behavior is correct but can be completed with some sort of switch, if toggled, the module will keep existing ipv4 and ipv6 when the config doesn't contains one. I will try an implementation to make it clear.
Due to my work (and my limited python skills :D), I do not reallly have the time for pushing this further.
I see that I let a message saying I will make an implementation with some sort of toggle variable, and I can’t really remember why I had this idea. It’s a pretty poor design.
I’m using this module since I « wrote » it in one of my role and had no problem with it.
If someone can help me with this, I would voluntarily do the same for an « lxd_storage » module which will be in the same vein (a port of the already existing lxd_profile module).
EDIT: I just tried to play around the module to see if things was missing. I noticed edge case where the idempotency was not respected, that leads me to find problems with the LXD endpoint (see).
So the LXD API would be fixed in version 3.3 and the idempotency should works as expected.
Another idea would be to force none value on subkeys presents in the running config and absent from the input config. Thus maintaining a pseudo « replace » mode for version before 3.3. Something that bogs me, is that you will always need to check the LXD version and that will add a call to the API.
I will happily do theses changes. But I would like to debate over what should be expected before implementing it if possible.
Any Ideas ?
I've been looking at your code and I have refactored it a bit. I also hardened the process that verifies whether an update is required and I don't think there is the need to use the
I tried to:
Let me know if there is any corner case that won't work for you. Keep in mind that this is my first time ever working with Ansible internals so maybe there could be some error.
Note: My environment is LXD 3.2 installed through Snappy on Debian Stretch amd64.
First thanks you for taking the time to implement this.
After testing your modifications everything seems fine to me, and I like how you handle the idempotency. However there is now one point of logic that I would warn about.
Let me try to get some context before since it is even not clear as I would want in my own head haha.
For the LXD API, theses two configs and any combination of theses keys are the same :
Today this isn’t clear since the PUT endpoint is working as if it was the PATCH one. So today something like :
Will ends up with this config as a result :
Where it should be :
We can not do anything about this, it is how LXD is working right now. But in a near future with the working endpoint, I think that ‘none’ value should not be treated the same as nothing.
And should be
And here is the trick, if you do this in the actuals LXD versions this would break the idempotency since the endpoint is not working properly.
Also when using other config keys, for example imagine that I use this config :
If I decide to remove the
How do you think thoses problematics should be handled ?
P.S : Also what do you think about adding
I don't think I fully understood your first example (I'm actually quite a noob in Ansible). I have the feeling that the issue is the LXD API not being consistent between read and write operations.
Regaring to the second example one way of solving it is either hardcoding the default configuration and checking the remote values against it (until the default values change and everything break) or it them from the server (if possible): if the remote's
Sorry for that, I just copy pasted my test playbook without further explanation.
It justs :
What I wanted to show there it's the fact that the correct way for LXD to handle this is to wipe off entirely the configuration then put ours in place.
Since those two configs :
are actually the same config, they should produce this exact same config :
And today this is only true regarding creation of a profile not when trying to replace it since it will only take the keys you supplied and set them up letting the present key you have not specified right there.
Concerning hardcoding things in the module, I thinks (as you thinks yourself I suppose) that certainly could lead to more pain than usefulness. I do not say that is not the solution, it will certainly keep away the problems we are facing now, but could introduce new as LXD evolves.
The more and more I'm thinking to this the more I think to take a different approach. We should take as a statement that the endpoint is not working great and could behave differently depending the version it uses.
We just take the two configs (running via GET, user supplied in module) and checks that they are exactly the same :
Then we just check again the current configuration and compare with with the initial one and if they are exactly the same :
We should stop trying to predict how the LXD API will behave on what we gonna give it to her, since it's unreliable and based on a set of rule that can change at any moment the LXD project decide it.
If I tell this is by placing me as an LXD user, not an ansible user. I want this module to act the same as if I would have used the API or the command line myself.
By all means, if I get more mess than I go towards a good solution, please don't hesitate to tell me. I'm new to all of this and don't even know if my thinkings are good or if I'm just wasting people's time.
Hi again, I've been thinking a while about your proposal and I have various concerns:
This will hardly happen, since even in most basic case, where a configuration is not provided, LXD will implicitly set
You already pointed out that a non existing parameter is different from a parameter having
A missing configuration parameter as far as I understand represents the default value provided by LXD.
After all, when you provide a configuration value in your Ansible play you are overriding either an existing value or a default value, aren't you?
Given all this and given the current state of the LXD API I would say that providing the module with a default configuration (hardcoding?) would solve most of the issue we're discussing.
I'd be way happier if there was a way to gather the default values from the LXD API itself, but apparently there's not.
It is not clear to me what kind of changes would you apply.
Regarding your concerns about the module being messy, I think that as far as the code is clear enough about what it is doing, and when not possible it is then carefully commented, it will be easier to track down potential issues (as you can see, I removed all the not necessary abstractions, such as the huge class that used to be). I'd love to see unit tests implemented too, I'm trying to figure out how can it be done.
Let me know if you think I missed the point or any of the things I wrote don't make sense.
You are right, that’s why I wanted to check between the
I agree, chances are this should not change frequently or at all.
You certainly misread, I was talking about the problematics we were talking about, not your implementation.
Now I understand more clearly your proposal. I actually like this idea because because of its simple design, but this also means that this module will never get a working
Sorry, I didn't mean to be rude :) I was actually talking about the design: even if the LXD API require us to write nasty workaround it's possible to keep the code clear and clean :)
So, what should we do?
This is a thing, I was not even thinking about the check mode.
I'm afraid I'm in the same position as you are. I don't know what is the better implementation, but to keep things ansible-esque, your proposition of hardcoding the values seems more preferable now. I would love some external opinions on this.