nuagenetworks/vspk-python

http headers incorrect after starting a second VSD session

Closed this issue · 2 comments

Problem description

Test automation requires the capability to define VSD session per enterprise to provide access to enterprises during test runs. It should be possible to perform CRUD actions on multiple enterprises over time.

Reproduction

  • create 2 enterprises on 1 vsd (cluster)
  • setup vsd session1 for enterprise1 (nume)
  • setup vsd session2 for enterprise1 (nume)
  • get first child of enterprise 1 using filter name=<enterprise_name> (nume object session1)

This result into a None object for first child. It looks like the HTTP header is garbeld with nume2 headers. This is clearly visible using robot framework output:

See log.html
session1: Return: <vspk.v6.nume.NUMe object at 0x7f0a64646208>
session2: Return: <vspk.v6.nume.NUMe object at 0x7f0a67fb4fd0>

Next the call to session1 to get enterprise by name from NUME object 0x7f0a64646208 (loggin session to se1-c1-v1 enterprise). From below excerpt you can see that the http header uses the wrong X-Nuage-Organization': 'se1-c5-v1' header. If the second session is not started, this test is successful.

Get the first child of a given kind
Start / End / Elapsed: 	20200806 22:13:59.973 / 20200806 22:14:00.163 / 00:00:00.190
22:13:59.974 	TRACE 	Arguments: [ obj=<vspk.v6.nume.NUMe object at 0x7f0a64646208> | attr='enterprises' | fltr='name is "se1-c1-v1"' ] 	
22:13:59.975 	INFO 	> GET https://124.252.253.200:8443/nuage/api/v6/enterprises 	
22:13:59.975 	DEBUG 	> headers: {'Content-Type': 'application/json', 'X-Nuage-Filter': 'name is "se1-c1-v1"', 'X-Nuage-Page': '0', 'X-Nuage-PageSize': '1', 'X-Nuage-Organization': 'se1-c5-v1', 'Authorization': 'XREST bWFyY3dvbGY6YzY2ZmRmM2ItMDVjZS00ZTA4LTlmOGMtMjQzYWU0MTQ5MGVj'} 	
22:13:59.976 	DEBUG 	> data:
  null 	
22:14:00.159 	DEBUG 	https://124.252.253.200:8443 "GET /nuage/api/v6/enterprises HTTP/1.1" 200 0 	
22:14:00.161 	INFO 	< GET https://124.252.253.200:8443/nuage/api/v6/enterprises  [200] 	
22:14:00.161 	DEBUG 	< headers: {'Server': 'Apache-Coyote/1.1', 'Pragma': 'No-cache', 'Cache-Control': 'no-cache', 'Expires': 'Wed, 31 Dec 1969 16:00:00 PST', 'Set-Cookie': 'rememberMe=deleteMe; Path=/nuage; Max-Age=0; Expires=Wed, 05-Aug-2020 20:14:01 GMT', 'X-Nuage-FilterType': 'none', 'X-Frame-Options': 'deny', 'X-Nuage-PageSize': '1', 'X-Nuage-Count': '0', 'Strict-Transport-Security': 'max-age=31536000', 'Access-Control-Expose-Headers': 'X-Nuage-Organization, X-Nuage-ProxyUser, X-Nuage-OrderBy, X-Nuage-FilterType, X-Nuage-Filter, X-Nuage-Page, X-Nuage-PageSize, X-Nuage-Count, X-Nuage-Custom, X-Nuage-ClientType, X-SSL-Client-CN, X-SSL-Client-ORG, X-SSL-Client-UID, X-SSL-Issuer-CN, X-SSL-Client-DN, X-SSL-Issuer-DN', 'X-Nuage-Filter': 'name is "se1-c1-v1"', 'Access-Control-Allow-Origin': '*', 'X-Nuage-OrderBy': 'name ASC', 'Content-Security-Policy': 'default-src', 'X-Nuage-Page': '0', 'X-XSS-Protection': '1', 'X-Content-Type-Options': 'nosniff', 'Referrer-Policy': 'origin', 'Content-Length': '0', 'Date': 'Thu, 06 Aug 2020 20:14:00 GMT'} 	
22:14:00.162 	DEBUG 	< data:
null 	
22:14:00.162 	TRACE 	Return: None

Attached:

  • Robot Framework using VSDLib
  • Python testscript including VSDLib class for Robot Framework
  • Robot Framework html log file

test_vsd.py.txt
log.html.txt
bug.robot.txt

Run Python script:

(nuage-cats-6.0.5) marcwolf@laptop:~/tmp> pytest --disable-warnings --verbose test_vsd.py 
==================================================== test session starts ====================================================
platform linux -- Python 3.6.10, pytest-6.0.1, py-1.9.0, pluggy-0.13.1 -- /home/marcwolf/venvs/nuage-cats-6.0.5/bin/python3
cachedir: .pytest_cache
rootdir: /home/marcwolf/tmp
collected 2 items                                                                                                           

test_vsd.py::test_get_first_child PASSED                                                                              [ 50%]
test_vsd.py::test_get_first_child_after_next_session FAILED                                                           [100%]

========================================================= FAILURES ==========================================================
__________________________________________ test_get_first_child_after_next_session __________________________________________

    def test_get_first_child_after_next_session():
        my_session1 = myVSD.start_vsd_session(url='https://124.252.253.200:8443',
                                         enterprise='se1-c1-v1',
                                         username='xxxxxxxxxxx',
                                         password='xxxxxxxxxxx')
        my_session2 = myVSD.start_vsd_session(url='https://124.252.253.200:8443',
                                         enterprise='se1-c5-v1',
                                         username='xxxxxxxxxx',
                                         password='xxxxxxxxxx')
        assert my_session1 is not None
        assert my_session2 is not None
        my_enterprise1 = myVSD.get_first_child(obj=my_session1, attr='enterprises', fltr='name is "se1-c1-v1"')
>       assert my_enterprise1.name == 'se1-c1-v1'
E       AttributeError: 'NoneType' object has no attribute 'name'

test_vsd.py:79: AttributeError
----------------------------------------------------- Captured log call -----------------------------------------------------
DEBUG    vsdlib:test_vsd.py:22 > VSDLib: Start VSD Session using url: https://124.252.253.200:8443
DEBUG    vsdlib:test_vsd.py:25 < VSDLib: Started VSD Session: <vspk.v6.nuvsdsession.NUVSDSession object at 0x7ff214e7c630>
DEBUG    vsdlib:test_vsd.py:22 > VSDLib: Start VSD Session using url: https://124.252.253.200:8443
DEBUG    vsdlib:test_vsd.py:25 < VSDLib: Started VSD Session: <vspk.v6.nuvsdsession.NUVSDSession object at 0x7ff214e7cb00>
================================================== short test summary info ==================================================
FAILED test_vsd.py::test_get_first_child_after_next_session - AttributeError: 'NoneType' object has no attribute 'name'

========================================== 1 failed, 1 passed, 3 warnings in 2.78s ==========================================

Hi @marcowenwolf,

Here's an example on how to use multiple sessions within the same script: https://github.com/nuagenetworks/vspk-examples/blob/master/python/multi-vsd_list_enterprises_domains_vms_structure_acls.py
This example uses different VSDs, but it is the same process for a single VSD environment, but have multiple users.

You need to use the python with statement (https://docs.python.org/3/reference/compound_stmts.html#the-with-statement) for this functionality.

I hope that helps.

Hallo Phillipe,

Thanks for the quick response. The 'with' suggestion works as a charm. So it looks like you can pass on the session object correctly, but using the session.user object causes issues re-using fetchers in case of multiple VSD sessions. For me it makes sense to use session object to pass on with 'with' statement.

class VSDLib:
    def start_vsd_session(self, url, username='csproot', password='csproot', enterprise='csp'):
        logger.debug('> VSDLib: Start VSD Session using url: {}'.format(url))
        nc = vsdk.NUVSDSession(username=username, password=password, enterprise=enterprise, api_url=url)
        session = nc.start()
        logger.debug('< VSDLib: Started VSD Session: {}'.format(session))
        return session

    def get_first_child(self, obj, attr, fltr=''):
        """Get the first child of a given kind

        Arguments:
        - obj -- the parent object
        - attr -- the attribute name of the child type list
        - fltr -- search filter

        Returns:
        - child object or None

        Examples:
        | ${r} = | Get First Child |  obj=root | attr=enterprises | fltr=name is "sdnlight-t1" |
        """
        with obj:
            logger.debug('< get_first_child: {}, {}, {}'.format(obj, attr, fltr))
            fetcher = obj.user.__dict__[attr]
            child = fetcher.get_first(filter=fltr)
            logger.debug('< Child: {}'.format(child))
            return child