The modules use a Parameter class that acts as a rudimentary two-way adapter. This class adapts data in the following ways:
This method has served most modules well, though for a number of modules, the abstraction has been difficult to work with. This largely stems from instances where:
In an attempt to settle the difficulty of using the “big adapter”, a different pattern is being tested where different classes (inheriting from a Parameters base class) will be used for the Ansible module parameters (ModuleParameters) and the REST API parameters (ApiParameters).
Additionally, the base Parameters class will be changing its base definition to remove the __getattr__ definition that it has. This definition has introduced an added layer of difficulty when debugging problems because it implicitly swallows and errors that may be raised by invalid “dot” attribute access. For example, self.want.baz where exceptions raised in baz may be swallowed. The only indication that this has happened is that a post q.q call will never happen.
... q.q("started here") self.want.baz # <-- raises exception internally q.q("ended here")
In the above (when the situation that I described above happens) the second q.q call will never happen. There will be no entry in the q log file location, but success of the module may actually happen! This is a can of worms, so we need to eliminate it before we regret it.
The base Parameters class will also have it’s signature changed from:
def __init__(self, params=None):
To a version that allows for a free-form of parameters and selectively chooses “special” parameters to do key things with. The new signature is:
def __init__(self, *args, **kwargs):
This allows us to expand on what the “valid” kwargs to the init method are. To begin with, there are two args that the base Parameters classes will know about. They are:
The former is no different than the way things work today; the exception being that you will need to be explicit when supplies params to a Parameters derived class because params is no longer just assumed to be the default. To change your code would require the following:
# legacy version self.want = Parameters(self.client.module.params)
Be changed to:
# current version self.want = ModuleParameters(params=self.client.module.params)
The later client parameter is new to the Parameters base class. In existing code bases it is possible to add this functionality to your concrete Parameter classes, but it is not obvious how, nor well-documented.
For example, today the following would need to be accomplished:
self.want = Parameters() self.want.client = self.client self.want.update(self.client.module.params)
You can change this to the following:
self.want = ModuleParameters( client=self.client, params=self.client.module.params )
Any concrete params class that inherits from the Parameters base class will be able to use the method shown above.
The client= feature seems like it was added only to make the above easier and more explicit. That, however, was more an unintended consequence than a goal. The real purpose for doing the above was for the following:
My assumptions here are based on the work that others have done in this area.
You see, the BIG-IQ code-base will require situations where the concrete Parameters classes themselves will be responsible for reading data from the remote device.
This is because, in many circumstances, we cannot know all of the resources and their attributes that we need to deal without, without querying for data using a resource attribute itself as input.
Surprisingly, we know this is going to be a problem, because we’ve already experienced it when making modules for F5 products.