source: genesis/tools/check-batch-cmd@ 13691

Last change on this file since 13691 was 11950, checked in by rick, 12 years ago

Automatic Nanostation MAC discovery to be put into genesis.

  • Property svn:executable set to *
File size: 8.1 KB
RevLine 
[10074]1#!/usr/bin/env python
2# vim:ts=2:et:sw=2:ai
3#
4# Check configs with remote addresses
5#
6# Rick van der Zwet <info@rickvanderzwet.nl>
7#
[10093]8import argparse
[10074]9import gformat
[10098]10import getpass
[10093]11import os
[10081]12import paramiko
[11950]13import socket
14import struct
[11053]15import subprocess
[10074]16import sys
[10093]17import time
[11729]18import logging
[10074]19
[11729]20logging.basicConfig(level=logging.INFO)
21logger = logging.getLogger(__name__)
22logging.getLogger("paramiko").setLevel(logging.WARNING)
23
[10098]24SSHPASS = None
[10892]25import netsnmp
[10081]26
[10074]27class CmdError(Exception):
28 pass
29
[10434]30class ConnectError(Exception):
31 pass
[10433]32
33
[10434]34
[10433]35def host_ssh_cmd(hostname, cmd):
[10434]36 try:
37 ssh = paramiko.SSHClient()
38 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
39 ssh.connect(hostname, username='root', password=SSHPASS,timeout=3)
40 stdin, stdout, stderr = ssh.exec_command(cmd)
41 stdout = stdout.readlines()
42 stderr = stderr.readlines()
43 ssh.close()
44 if stderr:
45 raise CmdError((stderr, stdout))
46 return stdout
[11053]47 except (socket.error, paramiko.AuthenticationException) as e:
[10434]48 raise ConnectError(e)
[10074]49
[10433]50def parse_ini(lines):
51 return dict(map(lambda x: x.strip().split('='),lines))
52
53def ubnt_probe(hostname):
54 items = parse_ini(host_ssh_cmd(hostname, 'cat /etc/board.info'))
55 print items
56
57
[10075]58def get_bridge_type(host):
[10081]59 """ Both NS and NS Mx uses a slighly different OID"""
60 var_list = netsnmp.VarList(
61 *map(lambda x: netsnmp.Varbind(x),
[11730]62 ['.1.2.840.10036.3.1.2.1.3.5', '.1.2.840.10036.3.1.2.1.3.6', '.1.2.840.10036.3.1.2.1.3.7','.1.3.6.1.2.1.1.5.0']))
[10075]63
[10099]64 sess = netsnmp.Session(Version=1, DestHost=host, Community='public', Timeout=2 * 100000, Retries=1)
[10075]65 retval = sess.get(var_list)
66 if sess.ErrorInd < 0:
[10098]67 raise CmdError('SNMP Failed -- [%(ErrorInd)s] %(ErrorStr)s (%(DestHost)s)' % vars(sess))
[11730]68 if not filter(None, retval):
69 return None
70 else:
71 return filter(None, retval)[0]
[10074]72
[10075]73
[11950]74# http://countergram.com/python-group-iterator-list-function
75def group_iter(iterator, n=2, strict=False):
76 """ Transforms a sequence of values into a sequence of n-tuples.
77 e.g. [1, 2, 3, 4, ...] => [(1, 2), (3, 4), ...] (when n == 2)
78 If strict, then it will raise ValueError if there is a group of fewer
79 than n items at the end of the sequence. """
80 accumulator = []
81 for item in iterator:
82 accumulator.append(item)
83 if len(accumulator) == n: # tested as fast as separate counter
84 yield tuple(accumulator)
85 accumulator = [] # tested faster than accumulator[:] = []
86 # and tested as fast as re-using one list object
87 if strict and len(accumulator) != 0:
88 raise ValueError("Leftover values")
[10082]89
[11950]90
91def get_bridge_mac(host):
92 """ Both NS and NS Mx uses a slighly different OID"""
93 var_list = netsnmp.VarList(
94 *map(lambda x: netsnmp.Varbind(x),
95 ['IF-MIB::ifDescr','IF-MIB::ifPhysAddress']))
96 sess = netsnmp.Session(Version=1, DestHost=host, Community='public', Timeout=6 * 100000, Retries=1)
97 retval = sess.walk(var_list)
98 if sess.ErrorInd < 0:
99 raise CmdError('SNMP Failed -- [%(ErrorInd)s] %(ErrorStr)s (%(DestHost)s)' % vars(sess))
100 if not filter(None, retval):
101 return None
102 else:
103 # We only have bridge configurations, so looking at bridge MAC addresses
104 mac_raw = dict(group_iter(retval,2))['br0']
105 return ':'.join(map(lambda x: "%02x" % x,struct.unpack("BBBBBB",mac_raw)))
106
107
108
109
110
[10433]111def node_check(host):
[11053]112 """ Using multiple connect methods to do some basic health checking as well"""
113
[10433]114 print "# Processing host", host
115 datadump = gformat.get_yaml(host)
116 output = host_ssh_cmd(datadump['autogen_fqdn'], 'cat /var/run/dmesg.boot')
[10093]117
[10433]118 # Get board Type
119 for line in [x.strip() for x in output]:
120 if line.startswith('CPU:'):
121 print line
122 elif line.startswith('Geode LX:'):
123 datadump['board'] = 'ALIX2'
124 print line
125 elif line.startswith('real memory'):
126 print line
127 elif line.startswith('Elan-mmcr'):
128 datadump['board'] = 'net45xx'
[10892]129 for iface_key in datadump['autogen_iface_keys']:
130 ifacedump = datadump[iface_key]
131 if ifacedump.has_key('ns_ip') and ifacedump['ns_ip']:
132 addr = ifacedump['ns_ip'].split('/')[0]
133 print "## Bridge IP: %(ns_ip)s at %(autogen_ifname)s" % ifacedump
134 try:
135 socket.create_connection((addr,80),2)
[11950]136 bridge_mac = get_bridge_mac(addr)
137 if bridge_mac:
138 datadump[iface_key]['ns_mac'] = bridge_mac
[10892]139 bridge_type = get_bridge_type(addr)
[11950]140 if bridge_type:
141 datadump[iface_key]['bridge_type'] = bridge_type
[10892]142 except (socket.timeout, socket.error) as e:
143 print "### %s (%s)" % (e, addr)
144 except paramiko.AuthenticationException:
145 print "### Conection failed (invalid username/password)"
146 except CmdError, e:
147 print "### Command error: %s" % e
[11053]148
149 try:
150 wl_release = subprocess.check_output(['snmpget', '-Oq', '-Ov', '-c', 'public', '-v2c',
[11729]151 datadump['autogen_fqdn'], 'UCD-SNMP-MIB::ucdavis.84.4.1.2.6.119.108.45.118.101.114.1'])
[11053]152 datadump['wl_release'] = int(wl_release.replace('"',''))
153 except subprocess.CalledProcessError, ValueError:
154 pass
[10433]155 gformat.store_yaml(datadump)
[10074]156
[10433]157
[10093]158def make_output(stdout, stderr):
159 def p(prefix, lines):
160 return ''.join(["#%s: %s" % (prefix, line) for line in lines])
161 output = p('STDOUT', stdout)
162 output += p('STDERR', stderr)
163 return output
[10083]164
[10093]165def ubnt_snmp(hostname):
166 lines = """\
167snmp.community=public
168snmp.contact=beheer@lijst.wirelessleiden.nl
169snmp.location=WL
170snmp.status=enabled\
171"""
[10095]172 cmd = 'mca-config get /tmp/get.cfg && grep -v snmp /tmp/get.cfg > /tmp/new.cfg && echo "%s" >> /tmp/new.cfg \
[10097]173 && mca-config activate /tmp/new.cfg 1>/dev/null 2>/dev/null && echo "ALL DONE"' % lines
[10093]174 ssh = paramiko.SSHClient()
175 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
176 ssh.connect(hostname, username='root', password=SSHPASS,timeout=3)
177 stdin, stdout, stderr = ssh.exec_command(cmd)
178 stdout = stdout.readlines()
179 stderr = stderr.readlines()
180 print make_output(stdout, stderr)
181 ssh.close()
[10084]182
[10093]183def ubnt_keys(hostname):
[10094]184 keys = open(os.path.join(gformat.NODE_DIR,'global_keys'),'r').read()
[10093]185 ssh = paramiko.SSHClient()
186 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
187 ssh.connect(hostname, username='root', password=SSHPASS,timeout=3)
188 cmd = 'test -d .ssh || mkdir .ssh;\
[10094]189 cat > .ssh/authorized_keys && \
[10093]190 chmod 0700 .ssh && \
[10094]191 chmod 0755 . && cfgmtd -p /etc -w'
[10093]192 stdin, stdout, stderr = ssh.exec_command(cmd)
[10094]193 stdin.write(keys)
194 stdin.flush()
195 stdin.channel.shutdown_write()
[10093]196 stdout = stdout.readlines()
197 stderr = stderr.readlines()
198 print make_output(stdout, stderr)
199 ssh.close()
[10085]200
[10093]201if __name__ == '__main__':
202 # create the top-level parser
203 parser = argparse.ArgumentParser(prog='Various WL management tools')
[10098]204 parser.add_argument('--ask-pass', dest="ask_pass", action='store_true', help='Ask password if SSHPASS is not found')
[10434]205 parser.add_argument('--filter', dest="use_filter", action='store_true', help='Thread the host definition as an filter')
[10093]206 subparsers = parser.add_subparsers(help='sub-command help')
207
[10433]208 parser_snmp = subparsers.add_parser('bridge', help='UBNT Bridge Management')
209 parser_snmp.add_argument('action', type=str, choices=['keys', 'snmp', 'probe'])
[10093]210 parser_snmp.add_argument('host',type=str)
[10433]211 parser_snmp.set_defaults(func='bridge')
[10093]212
[10433]213 parser_node = subparsers.add_parser('node', help='Proxy/Node/Hybrid Management')
214 parser_node.add_argument('action', type=str, choices=['check',])
215 parser_node.add_argument('host', type=str)
216 parser_node.set_defaults(func='node')
[10086]217
[10093]218 args = parser.parse_args()
[10087]219
[10098]220 try:
221 SSHPASS = os.environ['SSHPASS']
222 except KeyError:
223 print "#WARN: SSHPASS environ variable not found"
224 if args.ask_pass:
225 SSHPASS = getpass.getpass("WL root password: ")
226
227
[10434]228 if args.use_filter:
229 hosts = []
230 for host in gformat.get_hostlist():
231 if args.host in host:
232 hosts.append(host)
233 else:
234 hosts = [args.host]
[10433]235
[10434]236
237 for host in hosts:
238 try:
239 if args.func == 'bridge':
240 if args.action == 'keys':
241 ubnt_keys(host)
242 elif args.action == 'snmp':
243 ubnt_snmp(host)
244 elif args.action == 'probe':
245 ubnt_probe(host)
246 elif args.func == 'node':
247 if args.action == 'check':
248 node_check(host)
249 except ConnectError:
250 print "#ERR: Connection failed to host %s" % host
Note: See TracBrowser for help on using the repository browser.