{"id":1684,"date":"2023-05-15T22:29:18","date_gmt":"2023-05-15T21:29:18","guid":{"rendered":"http:\/\/192.168.1.213:8088\/?p=1684"},"modified":"2024-08-18T17:18:11","modified_gmt":"2024-08-18T15:18:11","slug":"cisco-dna-center-ansible-issue-dna-center-python-sdk-not-installed","status":"publish","type":"post","link":"http:\/\/192.168.1.213:8088\/cisco-dna-center-ansible-issue-dna-center-python-sdk-not-installed\/","title":{"rendered":"Cisco DNA Center Ansible – Issue: DNA Center Python SDK not installed"},"content":{"rendered":"\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t
\n\t\t\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t

\n\t\t\t\tTable of Contents\t\t\t<\/h4>\n\t\t\t\t\t\t\t
<\/i><\/div>\n\t\t\t\t
<\/i><\/div>\n\t\t\t\t\t<\/div>\n\t\t
\n\t\t\t
\n\t\t\t\t<\/i>\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t
Introduction<\/h5>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\t\t

I’m currently preparing for the Cisco Live lab, an event set to take place in Las Vegas in the upcoming weeks. As with any complex task, it’s been a journey of discovery, which, on this occasion, included a rather significant pitfall on my part.<\/p>

My error lay in the construction of the Dockerfiles, in conjunction with a less than robust config file. As if to add a cherry on top of this problematic sundae, recent updates to certain Python modules further complicated the situation. This confluence of factors resulted in a baffling issue: my Ansible playbooks steadfastly refused to execute.<\/p>

Despite every indication to the contrary, I was repeatedly confronted with an error message stating that the DNA Center SDK was not installed. I can assure you, this was certainly not the case. It felt like being accused of a crime I didn’t commit.<\/p>

In this article, I’ll take you through my missteps, the process of resolving this issue, and, most importantly, the lessons I’ve learned along the way. After all, acknowledging our mistakes and learning from them is a cornerstone of professional growth.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t

\n\t\t\t\t
\n\t\t\t
When the Reliable Becomes Unpredictable<\/h5>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t\t\t

At the core of this issue was my Dockerfile. This very same and simple Dockerfile had been a reliable ally during the Cisco Live session held in Amsterdam this past February, performing throughout the event.\u00a0<\/p>

As part of our standard practice, my team undertook the task of verifying the lab. The task at hand was straightforward: executing a simple Ansible playbook aimed at adding a site into the DNA Center. A routine procedure, we thought, but alas, it was not to be.<\/p>

To our surprise, we were consistently met with an error message:<\/p>

MSG:<\/strong><\/p>

‘DNA Center Python SDK is not installed. Execute ‘pip install dnacentersdk”<\/strong><\/p>

It was as if we were being told to fill up a car that already had a full tank of gas. We knew the DNA Center Python SDK was installed, yet the error message insisted otherwise. This set the stage for a deep dive into the problem, a journey of troubleshooting and discovery that would ultimately lead to valuable insights.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t

\n\t\t\t\t
\n\t\t\t
Dockerfile<\/h6>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t
\n\t\t\t\t
\n\t\t\t\t\t
\n\t\t\t
\n\t\t\t\t\n\t\t\t\t\tFROM ubuntu:22.04\n\nRUN apt-get update && \\\n  apt-get install -y gcc python3.11 git && \\\n  apt-get install -y python3-pip ssh && \\\n  pip3 install --upgrade pip && \\\n  pip3 install ansible && \\\n  pip3 install dnacentersdk && \\\n  pip3 install jmespath && \\\n  pip3 install pyats[full] && \\\n  pip3 install ansible-lint && \\\n  ansible-galaxy collection install cisco.dnac<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-6742af8 elementor-widget elementor-widget-heading\" data-id=\"6742af8\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h6 class=\"elementor-heading-title elementor-size-default\">Ansible Playbook<\/h6>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4c0b132 elementor-widget elementor-widget-code-highlight\" data-id=\"4c0b132\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"\" class=\"highlight-height language-markup line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-markup\">\n\t\t\t\t\t<xmp>- hosts: dnac_servers\n  gather_facts: false\n  tasks:\n  - name: Create area hierarchy\n    cisco.dnac.site_create:\n      site:\n        area:\n          name: Amsterdam\n          parentName: \"Global\"\n      type: \"area\"\n    register: site_creation\n\n  - name: Pause\n    pause:\n      seconds: 60\n\n  - name: Create building hierarchy\n    cisco.dnac.site_create:\n      site:\n        building:\n          name: CiscoLive\n          parentName: \"Global\/Amsterdam\"\n          latitude: 52.377956\n          longitude: 4.897070\n      type: \"building\" <\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-779b1d5 elementor-widget elementor-widget-heading\" data-id=\"779b1d5\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h5 class=\"elementor-heading-title elementor-size-default\">The surprising issue <\/h5>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-f950778 elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"f950778\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>Despite the persistent error message, a simple <code>pip list<\/code> command confirmed our initial belief &#8211; the Python module for the DNA Center SDK was indeed installed. Further investigation, using a highly verbose Ansible playbook execution (<code>-vvvv<\/code>), confirmed that the correct paths and Python versions were being utilized.<\/p><p>However, it was not until we opened Python and attempted to import the <code>ciscodnasdk<\/code> module that the real issue began to emerge. It appeared that Ansible had been somewhat of a misleading informant, not quite revealing the full extent of the problem.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-5d4a782 elementor-widget__width-initial elementor-widget elementor-widget-code-highlight\" data-id=\"5d4a782\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"32\" class=\"highlight-height language-markup line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-markup\">\n\t\t\t\t\t<xmp>\/usr\/bin\/python3.10\nPython 3.10.6 (main, Mar 10 2023, 10:55:28) [GCC 11.3.0] on linux\nType \"help\", \"copyright\", \"credits\" or \"license\" for more information.\n>>>  from dnacentersdk import api\n  File \"<stdin>\", line 1\n    from dnacentersdk import api\nIndentationError: unexpected indent\n>>> from dnacentersdk import api\nTraceback (most recent call last):\n  File \"\/usr\/local\/lib\/python3.10\/dist-packages\/requests_toolbelt\/_compat.py\", line 48, in <module>\n    from requests.packages.urllib3.contrib import appengine as gaecontrib\nImportError: cannot import name 'appengine' from 'requests.packages.urllib3.contrib' (\/usr\/local\/lib\/python3.10\/dist-packages\/urllib3\/contrib\/__init__.py)\n\nDuring handling of the above exception, another exception occurred:\n\nTraceback (most recent call last):\n  File \"<stdin>\", line 1, in <module>\n  File \"\/usr\/local\/lib\/python3.10\/dist-packages\/dnacentersdk\/__init__.py\", line 30, in <module>\n    from .api import DNACenterAPI\n  File \"\/usr\/local\/lib\/python3.10\/dist-packages\/dnacentersdk\/api\/__init__.py\", line 39, in <module>\n    from dnacentersdk.restsession import RestSession\n  File \"\/usr\/local\/lib\/python3.10\/dist-packages\/dnacentersdk\/restsession.py\", line 44, in <module>\n    from requests_toolbelt.multipart import encoder\n  File \"\/usr\/local\/lib\/python3.10\/dist-packages\/requests_toolbelt\/__init__.py\", line 12, in <module>\n    from .adapters import SSLAdapter, SourceAddressAdapter\n  File \"\/usr\/local\/lib\/python3.10\/dist-packages\/requests_toolbelt\/adapters\/__init__.py\", line 12, in <module>\n    from .ssl import SSLAdapter\n  File \"\/usr\/local\/lib\/python3.10\/dist-packages\/requests_toolbelt\/adapters\/ssl.py\", line 16, in <module>\n    from .._compat import poolmanager\n  File \"\/usr\/local\/lib\/python3.10\/dist-packages\/requests_toolbelt\/_compat.py\", line 50, in <module>\n    from urllib3.contrib import appengine as gaecontrib\nImportError: cannot import name 'appengine' from 'urllib3.contrib' (\/usr\/local\/lib\/python3.10\/dist-packages\/urllib3\/contrib\/__init__.py) <\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-849243f elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"849243f\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>As highlighted in line 32 something was wrong with the requests module. The cause is that the latest version of\u00a0<code>requests<\/code>\u00a0does not support urllib3 2.0.0which has been used by the dnacentersdk.\u00a0<\/p><p><a href=\"https:\/\/stackoverflow.com\/questions\/76175487\/sudden-importerror-cannot-import-name-appengine-from-requests-packages-urlli\" target=\"_blank\" rel=\"noopener\">https:\/\/stackoverflow.com\/questions\/76175487\/sudden-importerror-cannot-import-name-appengine-from-requests-packages-urlli<\/a><\/p><p>By using an older version of the urllib3 and requests-toolbelt module it could be fixed.<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0d91f43 elementor-widget elementor-widget-heading\" data-id=\"0d91f43\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h5 class=\"elementor-heading-title elementor-size-default\">Apply the fix and reevaluation of the Dockerfile<\/h5>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-0ee984f elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"0ee984f\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>With the root of the issue pinpointed, the path towards resolution was clear. The following Dockerfile exemplifies my solution, featuring older versions of <code>urllib3<\/code> and <code>requests-toolbelt<\/code> that were compatible with each other and the <code>dnacentersdk<\/code>. Furthermore, I made sure to specify the precise versions of <code>dnacentersdk<\/code> and the <code>galaxy collection<\/code>, as these also needed to be in harmony for successful execution.<\/p><p>Please follow the link below to see a table of compatible versions for the DNA Center Ansible playbooks with the dnacentersdk:<\/p><p><a href=\"https:\/\/galaxy.ansible.com\/cisco\/dnac\" target=\"_blank\" rel=\"noopener\">https:\/\/galaxy.ansible.com\/cisco\/dnac<\/a><\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-4b00758 elementor-widget__width-initial elementor-widget elementor-widget-code-highlight\" data-id=\"4b00758\" data-element_type=\"widget\" data-widget_type=\"code-highlight.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t<div class=\"prismjs-default copy-to-clipboard \">\n\t\t\t<pre data-line=\"10-11,13,17\" class=\"highlight-height language-markup line-numbers\">\n\t\t\t\t<code readonly=\"true\" class=\"language-markup\">\n\t\t\t\t\t<xmp>FROM ubuntu:22.04\n\nRUN apt-get update && \\\n    apt-get install -y apt-utils && \\\n    apt-get install -y gcc git && \\\n    apt-get install -y python3-venv python3-pip ssh\nRUN python3 -m venv \/root\/ansible\nRUN . \/root\/ansible\/bin\/activate && \\\n    pip install --upgrade pip && \\\n    pip install requests-toolbelt==0.10.1 && \\\n    pip install urllib3==1.26.15 && \\\n    pip install ansible && \\\n    pip install dnacentersdk==2.5.5 && \\\n    pip install jmespath && \\\n    pip install pyats[full] && \\ \n    pip install ansible-lint && \\ \n    ansible-galaxy collection install cisco.dnac:6.6.4<\/xmp>\n\t\t\t\t<\/code>\n\t\t\t<\/pre>\n\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-7b1214b elementor-widget elementor-widget-heading\" data-id=\"7b1214b\" data-element_type=\"widget\" data-widget_type=\"heading.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<h5 class=\"elementor-heading-title elementor-size-default\">Conclusion<\/h5>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<div class=\"elementor-element elementor-element-8847012 elementor-widget__width-initial elementor-widget elementor-widget-text-editor\" data-id=\"8847012\" data-element_type=\"widget\" data-widget_type=\"text-editor.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t\t\t\t\t<p>Reflecting<strong> on this journey, three fundamental principles stand out as critical lessons learned.<\/strong>\u00a0<\/p><p><strong>First<\/strong>, the importance of specifying exact versions when setting up a Docker container cannot be overstated. Docker containers are designed to provide a consistent environment, which is crucial for ensuring that applications run reliably when moved from one computing environment to another. However, the reproducibility and consistency of Docker containers can be compromised when we fail to specify exact versions of the software being installed. As our experience with the <code>dnacentersdk<\/code> and <code>urllib3<\/code> versions demonstrated, even minor differences in versions can lead to unforeseen conflicts and compatibility issues.<\/p><p><strong>Secondly<\/strong>, the value of using multiple RUN commands in Dockerfiles which I also introduced in the new version of the Dockerfile.<\/p><p>This approach provides several benefits: it improves readability, makes Dockerfiles easier to understand, and facilitates layering, which can optimize build times and efficient use of storage. Furthermore, structuring Dockerfiles with multiple RUN commands can help in debugging, as it allows for a more granular understanding of where problems might arise, as we experienced first-hand in our troubleshooting journey.<\/p><p><strong>Lastly<\/strong>, this experience emphasized the benefits of working with virtual environments. Virtual environments provide an isolated context, in which you can install specific versions of packages, without them interfering with other projects or system-wide installations. They offer an additional layer of safety, preventing package conflicts and ensuring a clean workspace for each of your projects.<\/p><p>In conclusion, Docker provides a powerful platform for consistent, reproducible application deployment, but as with any powerful tool, it requires careful handling. Attention to version specificity, the strategic use of multiple RUN commands in Dockerfiles, and the utilization of virtual environments are practices that contribute significantly to ensuring that Docker delivers on its promise of &#8220;Build, Ship, and Run Any App, Anywhere.&#8221;<\/p>\t\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<section class=\"elementor-section elementor-top-section elementor-element elementor-element-ca97fd7 elementor-section-boxed elementor-section-height-default elementor-section-height-default\" data-id=\"ca97fd7\" data-element_type=\"section\">\n\t\t\t\t\t\t<div class=\"elementor-container elementor-column-gap-default\">\n\t\t\t\t\t<div class=\"elementor-column elementor-col-100 elementor-top-column elementor-element elementor-element-3134bc4\" data-id=\"3134bc4\" data-element_type=\"column\">\n\t\t\t<div class=\"elementor-widget-wrap elementor-element-populated\">\n\t\t\t\t\t\t<div class=\"elementor-element elementor-element-59031b0 elementor-widget elementor-widget-html\" data-id=\"59031b0\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t<div class=\"elementor-widget-container\">\n\t\t\t<script type=\"text\/javascript\">\n(function($) {\n    $(window).load(function() {\n        $('.token.entity').each(function() {\n            var title = $(this).attr('title');\n            $(this).html(title);\n        })\n    });\n})(jQuery);\n<\/script>\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t<\/section>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>Table of Contents Introduction I&#8217;m currently preparing for the Cisco Live lab, an event set to take place in Las Vegas in the upcoming weeks. As with any complex task, it&#8217;s been a journey of discovery, which, on this occasion, included a rather significant pitfall on my part. My error lay in the construction of [&hellip;]<\/p>\n","protected":false},"author":2,"featured_media":1299,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"rank_math_lock_modified_date":false,"footnotes":""},"categories":[14,11,1],"tags":[],"class_list":["post-1684","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-cisco-technologies","category-automation","category-uncategorized"],"_links":{"self":[{"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/posts\/1684"}],"collection":[{"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/comments?post=1684"}],"version-history":[{"count":23,"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/posts\/1684\/revisions"}],"predecessor-version":[{"id":1971,"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/posts\/1684\/revisions\/1971"}],"wp:featuredmedia":[{"embeddable":true,"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/media\/1299"}],"wp:attachment":[{"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/media?parent=1684"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/categories?post=1684"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/192.168.1.213:8088\/wp-json\/wp\/v2\/tags?post=1684"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}