{"id":2427,"date":"2020-04-04T19:02:16","date_gmt":"2020-04-04T13:32:16","guid":{"rendered":"https:\/\/www.mka.in\/wp\/?p=2427"},"modified":"2020-05-28T13:38:59","modified_gmt":"2020-05-28T08:08:59","slug":"automated-bulk-command-execution-on-ssh-enabled-hosts-and-devices","status":"publish","type":"post","link":"https:\/\/www.mka.in\/wp\/automated-bulk-command-execution-on-ssh-enabled-hosts-and-devices\/","title":{"rendered":"Automated bulk command execution on SSH enabled hosts and devices"},"content":{"rendered":"\n<p>In the lockdown period, during pandemic, there is nothing much to do over weekend \ud83d\ude0f.<\/p>\n\n\n\n<p>Was just thinking of writing \u270d\ufe0f something, something useful&#8230;. then came up with an idea to address a common problem that many sysadmins and netadmins come across.<\/p>\n\n\n\n<p>At times, we want to run set of commands on many devices, hosts to perform some bulk changes, like run package updates, or get some information, like disk space, from set of servers.<\/p>\n\n\n\n<p>To achieve this, I have designed a simple json schema which has predefined credential sets (along with sudo\/enable password option), command alias and hosts mapped with credential set and list of command aliases.<\/p>\n\n\n\n<p>Also, I have added multiprocessing in code to run commands concurrently on &#8220;maxhosts&#8221; defined in json file.<\/p>\n\n\n\n<p>Here is config.json which defined all above stuff, pretty simple to understand.  <\/p>\n\n\n\n<p><strong>config.json:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\n{\n   \"maxhosts\": 5,\n   \"credsets\": {\n                 \"set1\": { \"username\": \"manish\", \"password\": \"abcd\", \"enpass\": \"xyz123\" },\n                 \"set2\": { \"username\": \"pi\", \"password\": \"Hello!\" }\n             },\n   \"cmds\": {\n             \"listfiles\": &#91;\"ls -l \/home\", 2],\n             \"diskspace\": &#91;\"df -h\", 1],\n             \"uptime\": &#91;\"uptime\", 1],\n             \"updatedebian\": &#91;\"apt-get update &amp;&amp; apt-get upgrade -y\", 10],\n             \"DebVer\": &#91;\"cat \/etc\/debian_version\", 1]\n           },\n   \"hosts\": {\n               \"192.168.0.6\": {\"type\": \"linux\", \"credset\": \"set1\", \"cmds\": &#91;\"updatedebian\", \"DebVer\"] },\n               \"192.168.0.7\": {\"type\": \"linux\", \"credset\": \"set1\", \"port\": 1223, \"cmds\": &#91;\"updatedebian\"] }\n            },\n   \"logging\": {\n                \"logfile\": \"\/home\/manish\/pymgt\/test.logs\"\n              }\n}<\/code><\/pre>\n\n\n\n<p>Above config.json is picked by following <strong>python code<\/strong> to first extract list of servers\/hosts. Then get associated credential and command list to be executed. Each command has delay factor in json (see netmiko documentation), default delay is about 100 sec, so if a command has delay factor of 3, then delay will be close to 300 sec. This is useful when you are performing upgrades etc. Output of login activity, command executions can be seen on standard output and later in logfile (defined in config.json).<\/p>\n\n\n\n<p><strong>pymgt.py:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/usr\/bin\/python3\nfrom netmiko import ConnectHandler\nfrom multiprocessing import Pool\nimport os\nimport json\nfrom time import strftime, localtime\nimport sys\n\ntry:\n   params = sys.argv&#91;1]\n   param, value = params.split('=')\n   if param != \"--play\":\n      sys.exit()\n   playconf = value\nexcept:\n   print(\"Usage: pymgt.py --play=&lt;json config>\")\n   sys.exit()\n\nwith open(playconf) as cfg:\n  cfgdata = json.load(cfg)\n\ndef CommitLogs(LogMessage):\n    logfile = cfgdata.get('logging').get('logfile')\n    try:\n         fopen = open(logfile, \"a\")\n         try:\n            fopen.write(LogMessage+\"\\n\")\n            fopen.close()\n         except:\n            print(\"Failed to write \",LogMessage)\n         return\n    except:\n         print(\"failed to open file\", logfile)\n    return\n\ndef injectcmds(device, cmds):\n   try:\n      net_connect = ConnectHandler(**device)\n      try:\n           LogMessage = strftime(\"%Y-%m-%d %H:%M:%S\", localtime())+\" \"+device.get('host')+\" Logged in...\"\n           print(LogMessage)\n           CommitLogs(LogMessage)\n           if device.get('secret') is not None:\n               net_connect.enable()\n               LogMessage = strftime(\"%Y-%m-%d %H:%M:%S\", localtime())+\" \"+device.get('host')+\" Enabled Elevated access....\"\n               print(LogMessage)\n               CommitLogs(LogMessage)\n           sshstatus = 0\n      except:\n           LogMessage = strftime(\"%Y-%m-%d %H:%M:%S\", localtime())+\" \"+device.get('host')+\";Cannot gain elevated access....\"\n           print(LogMessage)\n           CommitLogs(LogMessage)\n           sshstatus = -1\n\n   except:\n      LogMessage = strftime(\"%Y-%m-%d %H:%M:%S\", localtime())+\" \"+device.get('host')+\";SSH failed as \"+device.get('username')\n      print(LogMessage)\n      CommitLogs(LogMessage)\n      sshstatus = -1\n\n   if (sshstatus == 0):\n      for cmd in cmds:\n        waittime = 10\n        cmdobj = cfgdata.get('cmds').get(cmd)\n        cmd, delayfac = cmdobj&#91;0],cmdobj&#91;1]\n        print(cmd, delayfac)\n        try:\n          status = net_connect.send_command(cmd, delay_factor=delayfac)\n          LogMessage = strftime(\"%Y-%m-%d %H:%M:%S\", localtime())+\" \"+device.get('host')+\" Command:\"+cmd+\"\\nOutput:\"+status\n          print(LogMessage)\n          CommitLogs(LogMessage)\n        except:\n          LogMessage = strftime(\"%Y-%m-%d %H:%M:%S\", localtime())+\" \"+device.get('host')+\" \"+cmd+\" command failed\"\n          print(LogMessage)\n          CommitLogs(LogMessage)\n   return\n\ndef hostcmd(host):\n  hostdata = cfgdata.get('hosts').get(host)\n  type = hostdata.get('type')\n  cmds = hostdata.get('cmds')\n  port = hostdata.get('port')\n  if port is None:\n    port = 22\n  credset = hostdata.get('credset')\n\n  credparams = cfgdata.get('credsets').get(credset).keys()\n  username = cfgdata.get('credsets').get(credset).get(\"username\")\n  password = cfgdata.get('credsets').get(credset).get(\"password\")\n  if \"enpass\" in credparams:\n     enpass = cfgdata.get('credsets').get(credset).get(\"enpass\")\n  else:\n     enpass = None\n\n#  print(\"Enpass\", host, enpass)\n  if enpass is None:\n      device = {\n         'device_type': type,\n         'host': host,\n         'port': port,\n         'username': username,\n         'password': password,\n      }\n  else:\n      device = {\n         'device_type': type,\n         'host': host,\n         'port': port,\n         'username': username,\n         'password': password,\n         'secret': enpass,\n      }\n  injectcmds(device, cmds)\n\nhostlist = list(cfgdata.get('hosts').keys())\nmaxhosts = cfgdata.get('maxhosts')\nwith Pool(maxhosts) as p:\n    p.map(hostcmd, hostlist)\n<\/code><\/pre>\n\n\n\n<p>Lets execute it<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\nmanish@hq:~\/pymgt $ .\/pymgt.py --play=\/home\/manish\/pymgt\/config.json\n2020-04-12 08:40:56 192.168.0.6 Logged in...\n2020-04-12 08:40:57 cloud.mka.in Logged in...\n2020-04-12 08:40:58 192.168.0.6 Enabled Elevated access....\napt-get update &amp;&amp; apt-get upgrade -y 10\n2020-04-12 08:40:59 cloud.mka.in Enabled Elevated access....\napt-get update &amp;&amp; apt-get upgrade -y 10\n........\n........<\/code><\/pre>\n\n\n\n<p>Here is the sample log output saved in test.log (defined in config.json)<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>\ncat test.logs\n2020-04-04 18:39:03 192.168.0.7 Logged in...\n2020-04-04 18:39:03 192.168.0.6 Logged in...\n2020-04-04 18:39:04 192.168.0.7 Command:df -h\nOutput:Filesystem      Size  Used Avail Use% Mounted on\n\/dev\/root       7.1G  3.6G  3.3G  52% \/\ndevtmpfs        459M     0  459M   0% \/dev\ntmpfs           464M  115M  349M  25% \/dev\/shm\ntmpfs           464M  6.4M  457M   2% \/run\ntmpfs           5.0M  4.0K  5.0M   1% \/run\/lock\ntmpfs           464M     0  464M   0% \/sys\/fs\/cgroup\n\/dev\/mmcblk0p1  253M   53M  200M  21% \/boot\ntmpfs            93M     0   93M   0% \/run\/user\/1000\ntmpfs            93M     0   93M   0% \/run\/user\/999\ntmpfs            93M     0   93M   0% \/run\/user\/1001\n2020-04-04 18:39:04 192.168.0.6 Enabled Elevated access....\n2020-04-04 18:39:05 192.168.0.6 Command:ls -l \/home\nOutput:total 8\ndrwxr-xr-x  7 manish manish 4096 Apr  4 16:17 manish\ndrwxr-xr-x 17 pi     pi     4096 Apr  4 15:29 pi\n2020-04-04 18:39:06 192.168.0.6 Command:uptime\nOutput: 18:39:06 up  2:27,  6 users,  load average: 2.65, 2.56, 2.65<\/code><\/pre>\n\n\n\n<p>Hope readers it useful :-), specially sysadms and netadms.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In the lockdown period, during pandemic, there is nothing much to do over weekend \ud83d\ude0f. Was just thinking of writing \u270d\ufe0f something, something useful&#8230;. then came up with an idea to address a common problem that many sysadmins and netadmins come across. At times, we want to run set of commands on many devices, hosts [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":2435,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[15,53],"class_list":["post-2427","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-tech-bytes","tag-automation","tag-python"],"_links":{"self":[{"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/posts\/2427","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/comments?post=2427"}],"version-history":[{"count":13,"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/posts\/2427\/revisions"}],"predecessor-version":[{"id":2456,"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/posts\/2427\/revisions\/2456"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/media\/2435"}],"wp:attachment":[{"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/media?parent=2427"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/categories?post=2427"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.mka.in\/wp\/wp-json\/wp\/v2\/tags?post=2427"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}