{"id":2672,"date":"2018-07-03T22:59:19","date_gmt":"2018-07-04T03:59:19","guid":{"rendered":"http:\/\/osric.com\/chris\/accidental-developer\/?p=2672"},"modified":"2018-07-03T22:59:19","modified_gmt":"2018-07-04T03:59:19","slug":"attributeerror-module-paramiko-has-no-attribute-sshclient","status":"publish","type":"post","link":"https:\/\/osric.com\/chris\/accidental-developer\/2018\/07\/attributeerror-module-paramiko-has-no-attribute-sshclient\/","title":{"rendered":"AttributeError: module &#8216;paramiko&#8217; has no attribute &#8216;SSHClient&#8217;"},"content":{"rendered":"<p>I have a simple Python 3 script (I&#8217;m running Python 3.6.1, compiled from source) that does the following 3 things:<\/p>\n<ol>\n<li>Connects to remote server(s) and using <code>scp<\/code> to get files<\/li>\n<li>Processes the files.<\/li>\n<li>Connects to another remote server and uploads the processed files.<\/li>\n<\/ol>\n<p>I moved this script from a soon-to-be-retired CentOS 6 host to a CentOS 7 host, and when I ran it there I received the following error message:<\/p>\n<pre><code>AttributeError: module 'paramiko' has no attribute 'SSHClient'<\/code><\/pre>\n<p>The line number specified in the stack trace led me to:<\/p>\n<pre><code>ssh = paramiko.SSHClient()<\/code><\/pre>\n<p>First things first: Google the error. Someone else has seen this before. Sure enough, on <a href=\"https:\/\/stackoverflow.com\/questions\/25409977\/paramiko-module-object-has-no-attribute-error-sshclient\">StackOverflow I found Paramiko: Module object has no attribute error &#8216;SSHClient&#8217;<\/a><\/p>\n<p>But no, that&#8217;s not the problem I&#8217;m having.<\/p>\n<p>I tried to create the the simplest possible script that would reproduce the error:<\/p>\n<pre><code>#!\/usr\/bin\/python3\r\n\r\nimport paramiko\r\n\r\ndef main():\r\n        ssh = paramiko.SSHClient()\r\n\r\nif __name__ == \"__main__\":\r\n        main()<\/code><\/pre>\n<p>I ran it as a super-user and received no errors:<\/p>\n<pre><code>$ sudo \/usr\/bin\/python3 test.py<\/code><\/pre>\n<p>I ran it as myself, though, and it reproduced the error message. Maybe something with the user permissions?<\/p>\n<pre><code>$ ls -l \/usr\/local\/lib\/python3.6\/site-packages\/\r\ntotal 1168\r\n...\r\ndrwxr-x---.  3 root root   4096 May 29 14:25 paramiko\r\n...<\/code><\/pre>\n<p>Oh! From that you can see that unless you are the root user, or a member of the root group, there&#8217;s no way you can even <em>see<\/em> the files within the paramiko directory.<\/p>\n<p>What&#8217;s the default umask on the system?<\/p>\n<pre><code>$ umask\r\n0027<\/code><\/pre>\n<p>That explains it. Now, to fix it. I could probably just run:<\/p>\n<pre><code>$ sudo chmod -R 0755 \/usr\/local\/lib\/python3.6\/site-packages\/*<\/code><\/pre>\n<p>That should have been the end of that, problem solved! But in my case, installation of the pip modules had been handled by Ansible. I needed to fix the Ansible tasks to account for restrictive umask settings on future deployments. See the <code>umask<\/code> parameter in the <a href=\"https:\/\/docs.ansible.com\/ansible\/latest\/modules\/pip_module.html\">documentation for the Ansible pip module<\/a>. I updated the task:<\/p>\n<pre><code>- name: Install specified python requirements\r\n  pip:\r\n    executable: \/usr\/local\/bin\/pip3\r\n    requirements: \/tmp\/pip3-packages\r\n    umask: 0022<\/code><\/pre>\n<p>Running the playbook with that task, I received an error:<\/p>\n<pre><code>fatal: [trinculo.osric.net]: FAILED! =&gt; {\"changed\": false, \"details\": \"invalid literal for int() with base 8: '18'\", \"msg\": \"umask must be an octal integer\"}<\/code><\/pre>\n<p>Another helpful StackOverflow post suggested the value needed to be in quotes:<\/p>\n<pre><code>- name: Install specified python requirements\r\n  pip:\r\n    executable: \/usr\/local\/bin\/pip3\r\n    requirements: \/tmp\/pip3-packages\r\n    umask: \"0022\"<\/code><\/pre>\n<p>Now the playbook runs without error, but it doesn&#8217;t change the existing permissions. The task does nothing, since the Python pip modules are already installed. To really test the playbook, I need to clear out the existing modules first.<\/p>\n<p><strong>Warning:<\/strong> this breaks things!:<\/p>\n<pre><code>$ sudo rm -rf \/usr\/local\/lib\/python3.6\/site-packages\/*<\/code><\/pre>\n<p>I tried running the playbook again and received this error message:<\/p>\n<pre><code>stderr: Traceback (most recent call last):\\n  File \"\/usr\/local\/bin\/pip3\", line 7, in &lt;module&gt;\\n    from pip import main\\nModuleNotFoundError: No module named 'pip'\\n<\/code><\/pre>\n<p>I wasn&#8217;t able to run <code>pip<\/code> at all:<\/p>\n<pre><code>$ pip3\r\nTraceback (most recent call last):\r\n  File \"\/usr\/local\/bin\/pip3\", line 7, in &lt;module&gt;\r\n    from pip import main\r\nModuleNotFoundError: No module named 'pip'<\/code><\/pre>\n<p>Clearly, I had deleted something important! I reinstalled Python from the <a href=\"https:\/\/www.python.org\/ftp\/python\/3.6.1\/Python-3.6.1.tgz\">gzipped source tarball for Python 3.6.1<\/a> (newer versions available from <a href=\"https:\/\/www.python.org\/downloads\/source\/\">Python source releases<\/a>) and then everything worked as expected.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>I have a simple Python 3 script (I&#8217;m running Python 3.6.1, compiled from source) that does the following 3 things: Connects to remote server(s) and using scp to get files Processes the files. Connects to another remote server and uploads the processed files. I moved this script from a soon-to-be-retired CentOS 6 host to a &hellip; <a href=\"https:\/\/osric.com\/chris\/accidental-developer\/2018\/07\/attributeerror-module-paramiko-has-no-attribute-sshclient\/\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">AttributeError: module &#8216;paramiko&#8217; has no attribute &#8216;SSHClient&#8217;<\/span><\/a><\/p>\n","protected":false},"author":2,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[439,86],"tags":[423,504,506,358,505],"class_list":["post-2672","post","type-post","status-publish","format-standard","hentry","category-ansible","category-python","tag-ansible","tag-paramiko","tag-pip","tag-python","tag-umask"],"_links":{"self":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/2672","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/comments?post=2672"}],"version-history":[{"count":14,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/2672\/revisions"}],"predecessor-version":[{"id":2688,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/posts\/2672\/revisions\/2688"}],"wp:attachment":[{"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/media?parent=2672"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/categories?post=2672"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/osric.com\/chris\/accidental-developer\/wp-json\/wp\/v2\/tags?post=2672"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}