Skip to content
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

Adding the new login URL of NSO 5.7.5.1 #95

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

Mathias-gt
Copy link

In the version 5.7.5.1 of NSO the login URL "http://NSO_IP:8080/api" isn't available anymore. The new login URL is "http://NSO_IP:8080/restconf".

In the version 5.7.5.1 of NSO "/api" isn't available anymore. The new login URL is "/restconf".
@Mathias-gt Mathias-gt requested a review from a team as a code owner November 22, 2022 13:44
Copy link
Contributor

@lsheikal lsheikal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please add Unittest and pass log for this change.

@Mathias-gt
Copy link
Author

Mathias-gt commented Dec 26, 2022

please add Unittest and pass log for this change.

Here is the pass log :

root@camunda:~/pyats/test/test_nso# python3 -m unittest test_nso.py
...............
----------------------------------------------------------------------
Ran 15 tests in 0.693s

OK
root@camunda:~/pyats/test/test_nso# 

Here is the Unittest code I've adapted for my version of NSO (5.7.5.1) :

#!/bin/env python
""" Unit tests for the NSO REST implementation """

__copyright__ = "# Copyright (c) 2017 by cisco Systems, Inc. All rights reserved."
__author__ = "Dave Wapstra <dwapstra@cisco.com>"

import os
import unittest
import requests_mock

from pyats.topology import loader

from rest.connector import Rest

HERE = os.path.dirname(__file__)

@requests_mock.Mocker(kw='mock')
class test_nso_test_connector(unittest.TestCase):

    def setUp(self):
        self.testbed = loader.load(os.path.join(HERE, 'testbed.yaml'))
        self.device = self.testbed.devices['ncs']

    def test_connect(self, **kwargs):
        connection = Rest(device=self.device, alias='rest', via='rest')
        
        response_text ="""\
<!DOCTYPE html>
<html>
<head>
<title>404 Not Found</title>
</head>
<body>
<h1>Not Found</h1>
The requested URL /api was not found on this server.
<hr>
<address>  Server at localhost:8080 </address>
</body>
</html>"""

        kwargs['mock'].get('http://10.2.251.72:8080/api', text=response_text, status_code=404)

        response_text ="""\
<restconf xmlns="urn:ietf:params:xml:ns:yang:ietf-restconf">
    <data/>
    <operations/>
    <yang-library-version>2019-01-04</yang-library-version>
</restconf>
"""
        kwargs['mock'].get('http://10.2.251.72:8080/restconf', text=response_text)
        output = connection.connect(verbose=True).text    
        self.assertEqual(output, response_text)
        return connection


    def test_get(self, **kwargs):
        connection = self.test_connect()
  
        response_text = """\
<aaa xmlns="http://tail-f.com/ns/aaa/1.1"  xmlns:p17="http://tail-f.com/ns/aaa/1.1">
    <authentication>
        <users>
            <user>
                <name>admin</name>
                <uid>1000</uid>
                <gid>1000</gid>
                <password>$6$IHK0LOKQLsjqrYQc$9vWGPAD1dUa5..OnD8JBVTkWEyzF5Jc/6zrKGp4KXNxQV0uWMYWfzmjEGP1YWY/jWT/nOtxOZfw2aIASc4vjf/</password>
                <ssh_keydir>/home/admin/.ssh</ssh_keydir>
                <homedir>/home/admin</homedir>
            </user>
        </users>
    </authentication>
</aaa>
"""
        kwargs['mock'].get('http://10.2.251.72:8080/restconf/data/tailf-aaa:aaa', text=response_text)
        output = connection.get('/restconf/data/tailf-aaa:aaa', verbose=True).text
        
        self.assertEqual(output, response_text)
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_post(self, **kwargs):
        connection = self.test_connect()

        response_text = """\
<output xmlns='http://tail-f.com/ns/ncs'>
  <result>in-sync</result>
</output>
"""
        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/check-sync'
        kwargs['mock'].post('http://10.2.251.72:8080%s' % url, text=response_text)

        output = connection.post(url, content_type='application/yang-data+xml', verbose=True).text
        self.assertEqual(output, response_text)
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_post_dict_payload_without_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {'abc': 'def'}

        response_text = """\
<output xmlns='http://tail-f.com/ns/ncs'>
  <result>in-sync</result>
</output>
"""

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/check-sync'
        kwargs['mock'].post('http://10.2.251.72:8080%s' % url, text=response_text)
        try:
            output = connection.post(url, payload, verbose=True).text
        except AssertionError as e:
            self.assertEqual(str(e), 'content_type parameter required when passing dict')
        connection.disconnect()

        self.assertEqual(connection.connected, False)

    def test_post_dict_payload_with_json_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {'abc': 'def'}

        response_text = """\
<output xmlns='http://tail-f.com/ns/ncs'>
  <result>in-sync</result>
</output>
"""

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/check-sync'
        kwargs['mock'].post('http://10.2.251.72:8080%s' % url, text=response_text)
        output = connection.post(url, payload, content_type='application/yang-data+json', verbose=True).text
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_post_dict_payload_with_xml_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {'abc': 'def'}

        response_text = """\
<output xmlns='http://tail-f.com/ns/ncs'>
  <result>in-sync</result>
</output>
"""

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/check-sync'
        kwargs['mock'].post('http://10.2.251.72:8080%s' % url, text=response_text)
        output = connection.post(url, payload, content_type='application/yang-data+xml', verbose=True).text
        connection.disconnect()

        self.assertEqual(connection.connected, False)



    def test_patch(self, **kwargs):
        connection = self.test_connect()

        payload = """\
{
  "tailf-ned-cisco-ios-xr:unicast": {
    "routes-if": [
      {
        "net": "fe80::1/128",
        "interface": "Gi0/0/0/0"
      }
    ]
  }
}
"""

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].patch('http://10.2.251.72:8080%s' % url, status_code=204)
        output = connection.patch(url, payload, verbose=True).text
        self.assertEqual(output, '')
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_patch_dict_payload_without_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {
          "tailf-ned-cisco-ios-xr:unicast": {
            "routes-if": [
              {
                "net": "fe80::1/128",
                "interface": "Gi0/0/0/0"
              }
            ]
          }
        }

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].patch('http://10.2.251.72:8080%s' % url, status_code=204)
        try:
            output = connection.patch(url, payload, verbose=True).text
        except AssertionError as e:
            self.assertEqual(str(e), 'content_type parameter required when passing dict')
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_patch_dict_payload_with_json_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {
          "tailf-ned-cisco-ios-xr:unicast": {
            "routes-if": [
              {
                "net": "fe80::1/128",
                "interface": "Gi0/0/0/0"
              }
            ]
          }
        }

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].patch('http://10.2.251.72:8080%s' % url, status_code=204)
        output = connection.patch(url, payload, content_type='application/yang-data+json', verbose=True).text
        self.assertEqual(output, '')
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_patch_dict_payload_with_xml_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {
          "tailf-ned-cisco-ios-xr:unicast": {
            "routes-if": [
              {
                "net": "fe80::1/128",
                "interface": "Gi0/0/0/0"
              }
            ]
          }
        }

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].patch('http://10.2.251.72:8080%s' % url, status_code=204)
        output = connection.patch(url, payload, content_type='application/yang-data+xml', verbose=True).text
        self.assertEqual(output, '')
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_put(self, **kwargs):
        connection = self.test_connect()

        payload = """\
{
  "tailf-ned-cisco-ios-xr:unicast": {
    "routes-if": [
      {
        "net": "fe80::2/128",
        "interface": "Gi0/0/0/0"
      },
      {
        "net": "fe80::3/128",
        "interface": "Gi0/0/0/0"
      }
    ]
  }
}
"""

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].put('http://10.2.251.72:8080%s' % url, status_code=204)
        output = connection.put(url, payload, verbose=True).text
        self.assertEqual(output, '')
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_put_dict_payload_without_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {
          "tailf-ned-cisco-ios-xr:unicast": {
            "routes-if": [
              {
                "net": "fe80::2/128",
                "interface": "Gi0/0/0/0"
              },
              {
                "net": "fe80::3/128",
                "interface": "Gi0/0/0/0"
              }
            ]
          }
        }

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].put('http://10.2.251.72:8080%s' % url, status_code=204)
        try:
            output = connection.put(url, payload, verbose=True).text
        except AssertionError as e:
            self.assertEqual(str(e), 'content_type parameter required when passing dict')
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_put_dict_payload_with_json_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {
          "tailf-ned-cisco-ios-xr:unicast": {
            "routes-if": [
              {
                "net": "fe80::2/128",
                "interface": "Gi0/0/0/0"
              },
              {
                "net": "fe80::3/128",
                "interface": "Gi0/0/0/0"
              }
            ]
          }
        }

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].put('http://10.2.251.72:8080%s' % url, status_code=204)
        output = connection.put(url, payload, content_type='application/yang-data+json', verbose=True).text
        connection.disconnect()

        self.assertEqual(connection.connected, False)


    def test_put_dict_payload_with_xml_content_type(self, **kwargs):
        connection = self.test_connect()

        payload = {
          "tailf-ned-cisco-ios-xr:unicast": {
            "routes-if": [
              {
                "net": "fe80::2/128",
                "interface": "Gi0/0/0/0"
              },
              {
                "net": "fe80::3/128",
                "interface": "Gi0/0/0/0"
              }
            ]
          }
        }

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].put('http://10.2.251.72:8080%s' % url, status_code=204)
        output = connection.put(url, payload, content_type='application/yang-data+xml', verbose=True).text
        connection.disconnect()

        self.assertEqual(connection.connected, False)



    def test_delete(self, **kwargs):
        connection = self.test_connect()

        url = '/restconf/data/tailf-ncs:devices/device=CSG022221/config/tailf-ned-cisco-ios-xr:router/static/address-family/ipv6/unicast'
        kwargs['mock'].delete('http://10.2.251.72:8080%s' % url, status_code=204)
        output = connection.delete(url, verbose=True).text
        self.assertEqual(output, '')
        connection.disconnect()

        self.assertEqual(connection.connected, False)




if __name__ == "__main__":
    import sys
    import logging

    logging.basicConfig(stream=sys.stderr, level=logging.WARNING, format="%(asctime)s [%(levelname)8s]:  %(message)s")
    logger = logging.getLogger('rest')
    logger.setLevel(logging.DEBUG)
    unittest.main()

@Mathias-gt
Copy link
Author

Here is the pass log for all the unittest tests :

root@camunda:/usr/local/lib/python3.8/dist-packages/rest/connector/tests# ll
total 228
drwxr-sr-x 3 root staff  4096 nov.  22 09:47 ./
drwxr-sr-x 5 root staff  4096 nov.  22 15:33 ../
-rw-r--r-- 1 root staff     0 nov.  22 09:47 __init__.py
drwxr-sr-x 2 root staff  4096 nov.  22 09:47 __pycache__/
-rw-r--r-- 1 root staff 13088 nov.  22 09:47 test_apic_cobra.py
-rw-r--r-- 1 root staff 14476 nov.  22 09:47 test_apic.py
-rw-r--r-- 1 root staff  2622 nov.  22 09:47 testbed.yaml
-rw-r--r-- 1 root staff 18378 nov.  22 09:47 test_bigip.py
-rw-r--r-- 1 root staff 12620 nov.  22 09:47 test_elasticsearch.py
-rw-r--r-- 1 root staff 63814 nov.  22 09:47 test_iosxe.py
-rw-r--r-- 1 root staff 22013 nov.  22 09:47 test_nd.py
-rw-r--r-- 1 root staff 12610 nov.  22 09:47 test_nso.py
-rw-r--r-- 1 root staff 13769 nov.  22 09:47 test_nxos.py
-rw-r--r-- 1 root staff  6564 nov.  22 09:47 test_viptela.py
-rw-r--r-- 1 root staff 12634 nov.  22 09:47 test_webex.py
root@camunda:/usr/local/lib/python3.8/dist-packages/rest/connector/tests# python3 -m unittest
....................sssssssssssadmin
.admin
.admin
.admin
.admin
............................................................................................................admin
.admin
.admin
.admin
..admin
.admin
...admin
....................
----------------------------------------------------------------------
Ran 172 tests in 14.508s

OK (skipped=11)
root@camunda:/usr/local/lib/python3.8/dist-packages/rest/connector/tests#

Comment on lines 138 to 139
log.debug("Response: {c} {r}, headers: {h}".format(c=response.status_code,
r=response.reason, h=response.headers))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please move the debug logging into the for loop

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@dwapstra
Copy link
Contributor

dwapstra commented Jun 8, 2023

Here is the Unittest code I've adapted for my version of NSO (5.7.5.1) :

Can you add an additional test with the code changes? (Have not compared above with the code but I assume you made changes)

Moove the debug inside the for
@Mathias-gt
Copy link
Author

Here is the Unittest code I've adapted for my version of NSO (5.7.5.1) :

Can you add an additional test with the code changes? (Have not compared above with the code but I assume you made changes)

Here is the tests with the code changes :

tipadmin@srv1tip-auto:~/.local/lib/python3.10/site-packages/rest/connector/tests$ sed -n -e 120,140p ../libs/nso/implementation.py 

        self.login_urls = ['{f}/api'.format(f=self.base_url),'{f}/restconf'.format(f=self.base_url)]


        log.info("Connecting to '{d}' with alias "
                 "'{a}'".format(d=self.device.name, a=self.alias))

        username, password = get_username_password(self)

        self.session = requests.Session()
        self.session.auth = (username, password)

        # Connect to the device via requests
        for login_url in self.login_urls:
            response = self.session.get(login_url, timeout=timeout)
            log.debug("Response: {c} {r}, headers: {h}".format(c=response.status_code,
                r=response.reason, h=response.headers))
            if response.status_code == requests.codes.ok:
                self.login_url = login_url
                break
        output = response.text
tipadmin@srv1tip-auto:~/.local/lib/python3.10/site-packages/rest/connector/tests$ python3 -m unittest
..................../usr/lib/python3/dist-packages/pkg_resources/__init__.py:116: PkgResourcesDeprecationWarning: 1.1build1 is an invalid version and will not be supported in a future release
  warnings.warn(
/usr/lib/python3/dist-packages/pkg_resources/_vendor/packaging/version.py:111: DeprecationWarning: Creating a LegacyVersion has been deprecated and will be removed in the next major release
  warnings.warn(
/usr/lib/python3/dist-packages/pkg_resources/__init__.py:116: PkgResourcesDeprecationWarning: 0.1.43ubuntu1 is an invalid version and will not be supported in a future release
  warnings.warn(
sssssssssss...............................................................................................................................................................
----------------------------------------------------------------------
Ran 190 tests in 21.359s

OK (skipped=11)
tipadmin@srv1tip-auto:~/.local/lib/python3.10/site-packages/rest/connector/tests$ python3 -m unittest test_nso.py 
...............
----------------------------------------------------------------------
Ran 15 tests in 1.364s

OK
tipadmin@srv1tip-auto:~/.local/lib/python3.10/site-packages/rest/connector/tests$ 

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants