napalm-automation-community / napalm-asa

napalm-asa

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Support ASA Multi-Context Mode

DiogoAndre opened this issue · comments

commented

I'm creating this issue following @Enzzzy's suggestion on Slack. We will try to provide some POC code and discuss what is the best way to implement multi-context support for this driver.

This is some poc code for multicontext support.
It is roughly implemented like this: When open() is called, we perform a check to see if the device is in multicontext mode. If it is in mutlicontext mode, then we collect the contexts and set the multicontext & contexts attributes.

I've then modified the get_config() & cli() methods to accept an optional context parameter.

import napalm
import napalm_asa

drv = napalm.get_network_driver('asa')
dev = drv(hostname='192.168.2.10', username='cisco', password='cisco')
dev.open()
dev.get_config(context='customer_1')

This is just an example of how it could work. Not sure if it is a good idea to add optional arguments to "standard" napalm methods.

wvandeun@cdb3582

Hi @dbarrosop
I think we want some feedback/review from you here.
We are looking to implement multicontext support. I'm assuming you are familiar with the context concept, if not I can explain further.
The rest api calls that you make to an ASA in multicontext mode, are always targeting the context which is configured as admin context. If you want to target another context, then you have to specify the context name as a URL parameter (/api/endpoint?context=name).
I wanted to implement this in the driver by accepting an optional context parameter for the getters that support it, with some additional checks/safeguards. You can find an example here wvandeun@cdb3582
The get_config() method is modified to accept a context.
This means that the signature of the standard napalm methods change. Is that something you think is acceptable for a community driver?

So that's certainly going to break the integration. We have discussed accepting everywhere **kwargs for cases like the one you described but that's a big change and we haven't had the time/consensus to do so. What I'd suggest is, either, you bring this topic to the main napalm org and see what comes out of there or you use an optional_arg. So that'd be:

device = ASADriver(...., optional_args={"context": a_context})
device.get_config()

Alternatively:

device = ASADriver(....)
device.context = "a_context"
device.get_config()

Or both.

I, mean, you can do what you think is best but what you are proposing today (without the general kwargs solution) this is going to break if someone tries to use this in a multivendor setup (whch is kind of the point of napalm after all :P)

That's why we were involving you, we don't want anything to break.

I do like a combination of both alternatives that you bring up.
Optionally accept a context argument when instantiating a driver object & a setter for the context property.
I think we should go for that 'solution' until the general kwargs solution gets implemented.

Thanks for the feedback

I've removed the optional parameter from the methods.
Added a context property + safeguard
Context property needs to be set to execute subsequent methods on that specific context.

wvandeun@86ecaf9

commented

Looking good! I agree, let's continue with the optional_arg + setter for context! thanks for the input @dbarrosop

can you have a look here wvandeun@4e31c22 ?

I've implemented multi context support for all getters as discussed above.
I've added a file _MC_INCOMPATIBLE_ENDPOINTS.py that holds all the endpoints that do not support multi context calls. Those calls will be directed to the 'admin' context of the device (example '/monitoring/serialnumber').
I've also introduced a optional arg to the driver called delay. This delay is used for a problem that I have encountered when calling /interfaces/physical endpoint. If you send an API call to quickly after you called /interfaces/physical then the ASA resets the HTTPS connection, with providing an HTTP response. Delaying the subsequent call helps. I've defaulted is to 500ms, because that seems to work for me. But it's configurable as it is an optional_arg. It's not ideal, but I'll try to open a TAC case when tile allows.

commented

Nice work @Enzzzy!

I don't have any major concerns about the code, I think you have a good direction that we can follow to implement this.

Just a couple of notes(sorry if I'm a bit picky):

On L240 I get why you named the method _proxy_to_other_context but from the 'reading the code' point of view, it may not clear that we are proxying the request to a given context through the Admin context. What do you think about renaming it to something more direct, like _add_context_to_endpoint or _use_context_in_request?

Also on the same method, I usually prefer to write things more explicitly, even if it means writing a bit more code. How do you feel about refactoring the following bit?

return self.multicontext and \
               self._context and \
               endpoint not in MC_INCOMPATIBLE_ENDPOINTS

It may be just my weak python skills talking, but I would prefer that the return value be more explicit.

oh, and also on L363, the current form works, but the linter will complain about it, better change to something like:

if context not in self.contexts:

Other minor things like extra spaces and such we can fix when working on the tests.

Thank you for getting this rolling!

I've made the suggested improvements and fixed the linting issues.
wvandeun@508adcf

Todo: write tests

commented

👏

Speaking of tests, I was bitten by an issue recently on #22. I will describe the problem there and the fix I could figure out so far.