source: src/django_gheat/gheat/management/commands/import_netstumbler.py@ 9623

Last change on this file since 9623 was 9623, checked in by rick, 13 years ago

Merge and migrate all files into common files to get rid of all duplicate codes.

  • Property svn:executable set to *
File size: 7.4 KB
Line 
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3#
4# Script for importing .ns1 files (Netstumber output)
5#
6# Rick van der Zwet <info@rickvanderzwet.nl>
7#
8from django.core.management.base import BaseCommand,CommandError
9from django.db.utils import IntegrityError
10from optparse import OptionParser, make_option
11from gheat.models import *
12from lxml import etree
13import datetime
14import gzip
15import os
16import sys
17import logging
18
19from collections import defaultdict
20
21from netstumbler import parse_netstumbler
22from import_droidstumbler import bulk_sql,get_organization_id_by_ssid
23
24logger = logging.getLogger(__name__)
25logger.setLevel(logging.INFO)
26
27# Open files for reading
28def open_file(file):
29 if file.endswith('.gz'):
30 return gzip.open(file,'rb')
31 else:
32 return open(file,'rb')
33
34
35
36def import_accespoints(ap_pool, counters):
37 # Determine which Accespoints to add
38 bssid_list_present = Accespoint.objects.filter(mac__in=ap_pool.keys()).\
39 values_list('mac', flat=True)
40 bssid_list_insert = set(ap_pool.keys()) - set(bssid_list_present)
41
42 # Create a bulk import list and import
43 if bssid_list_insert:
44 sql_values = []
45 for bssid in bssid_list_insert:
46 ssid, encryption = ap_pool[bssid]
47 # Special trick in SSID ts avoid escaping in later stage
48 item = str((bssid.upper(),ssid.replace('%','%%'),encryption,
49 get_organization_id_by_ssid(ssid)))
50 sql_values.append(item)
51 counters['ap_added'] = bulk_sql('gheat_accespoint (`mac`, `ssid`,\
52 `encryptie`, `organization_id`)',sql_values)
53 return counters
54
55
56
57def import_metingen(meetrondje, meting_pool, counters):
58 # Temponary holders
59 bssid_failed = defaultdict(int)
60
61 bssid_list = [x[0] for x in meting_pool.keys()]
62 # Build mapping for meting import
63 mac2id = {}
64 for mac,id in Accespoint.objects.filter(mac__in=bssid_list).\
65 values_list('mac','id'):
66 mac2id[mac] = int(id)
67
68 clients = {}
69 for mac in WirelessClient.objects.filter(mac__in=bssid_list).\
70 values_list('mac',flat=True):
71 clients[mac] = True
72
73 sql_values = []
74 for (bssid,lat,lon),signals in meting_pool.iteritems():
75 if clients.has_key(bssid):
76 counters['meting_ignored'] += len(signals)
77 elif not mac2id.has_key(bssid):
78 counters['meting_failed'] += len(signals)
79 bssid_failed[bssid] += len(signals)
80 else:
81 item = str((int(meetrondje.id),mac2id[bssid],float(lat),\
82 float(lon),max(signals)))
83 sql_values.append(item)
84
85 for bssid,count in sorted(bssid_failed.items(),
86 key=lambda item: item[1], reverse=True):
87 logger.debug("Missing BSSID %s found %3s times", bssid, count)
88
89 if sql_values:
90 counters['meting_added'] = bulk_sql('gheat_meting (`meetrondje_id`,\
91 `accespoint_id`, `lat`, `lng`, `signaal`)',sql_values)
92 return counters
93
94
95
96def process_ns1(filename):
97 data = parse_netstumbler(open_file(filename))
98
99 counters = {
100 'ap_added' : 0, 'ap_total' : 0,
101 'ap_failed' : 0, 'ap_ignored' : 0,
102 'client_added' : 0, 'client_total' : 0,
103 'client_failed' : 0, 'client_ignored' : 0,
104 'meting_added' : 0, 'meting_total' : 0,
105 'meting_failed' : 0, 'meting_ignored' : 0
106 }
107
108 # Temponary holders
109 meting_pool = defaultdict(list)
110 ap_pool = {}
111
112 for ap in data['aps']:
113 # XXX: How is encryption coded?
114 encryption = False
115 ap_pool[ap['BSSID']]= (ap['SSID'], encryption)
116 for point in ap["measurements"]:
117 counters['meting_total'] += 1
118 if point['LocationSource'] == 0:
119 logger.debug("No GPS Coordinates found for BSSID %s @ %s",
120 ap['BSSID'], point['Time'])
121 counters['meting_ignored'] += 1
122 continue
123 # We store all values found, avg or max will be done later on
124 key = (ap['BSSID'], point["Latitude"], point["Longitude"])
125
126 # Known measurement error
127 if point['Signal'] == -32767: continue
128
129 # XXX: Signal need properly be a relation of signal_dbm and noice_dbm
130 signaal= 100 + point['Signal']
131 if signaal > 100 or signaal < 0:
132 logger.warning("Signal %s is not valid entry for BSSID %s @ %s",
133 point['Signal'], ap['BSSID'], point['Time'])
134 continue
135 meting_pool[key].append(signaal)
136
137 return (counters, ap_pool, meting_pool)
138
139
140
141
142class Command(BaseCommand):
143 args = '<netstumber.ns1>[.gz] [netstumber2.ns1[.gz] netstumber3.ns1[.gz] ...]'
144 option_list = BaseCommand.option_list + (
145 make_option('-k', '--kaart', dest='kaart', default='onbekend',
146 help="Kaart gebruikt"),
147 make_option('-m', '--meetrondje', dest='meetrondje', default=None),
148 make_option('-g', '--gebruiker', dest='gebruiker', default='username',
149 help='Naam van de persoon die de meting uitgevoerd heeft'),
150 make_option('-e', '--email', dest='email', default='foo@bar.org',
151 help='Email van de persoon die de meting uitgevoerd heeft'),
152 make_option('-d', '--datum', dest='datum', default=None,
153 help="Provide date in following format: '%Y%m%d-%H-%M-%S-1', by \
154 default it will be generated from the filename"),
155 )
156
157 def handle(self, *args, **options):
158 if options['verbosity'] > 1:
159 logger.setLevel(logging.DEBUG)
160 if len(args) == 0:
161 self.print_help(sys.argv[0],sys.argv[1])
162 raise CommandError("Not all arguments are provided")
163
164 # Please first the netxml and then the gpsxml files
165 sorted_args = [x for x in args if "ns1" in x]
166 remainder = list(set(args) - set(sorted_args))
167 args = sorted_args + remainder
168 logger.debug("Parsing files in the following order: %s", args)
169
170 for filename in args:
171 if not os.path.isfile(filename):
172 raise CommandError("file '%s' does not exists" % filename)
173
174 def process_date(datestr):
175 try:
176 # Kismet-20110805-15-37-30-1
177 return datetime.datetime.strptime(datestr,'%Y%m%d-%H-%M-%S-1')
178 except ValueError:
179 raise CommandError("Invalid date '%s'" % options['datum'])
180
181 for filename in args:
182 logger.info("Processing '%s'" % filename)
183 if 'ns1' in filename:
184 if options['datum'] == None:
185 datestr = os.path.basename(filename).lstrip('Kismet-').\
186 rstrip('.gz').rstrip('.gpsxml').rstrip('.netxml').rstrip('.ns1')
187 datum = process_date(datestr)
188 elif options['datum'] == 'now':
189 datum = datetime.datetime.now()
190 else:
191 datum = process_date(options['datum'])
192
193 # Meetrondje from filename if needed
194 if options['meetrondje'] == None:
195 meetrondje = os.path.basename(filename).rstrip('.gz').rstrip('.ns1')
196 else:
197 meetrondje = options['meetrondje']
198
199 # Create meetrondje object
200 g, created = Gebruiker.objects.get_or_create(naam=options['gebruiker'],
201 email=options['email'])
202 a, created = Apparatuur.objects.get_or_create(kaart=options['kaart'])
203 mr, created = MeetRondje.objects.get_or_create(datum=datum,
204 naam=meetrondje, gebruiker=g, apparatuur=a)
205 logger.info('Meetrondje: %s @ %s' % (meetrondje, datum))
206 if not created:
207 logger.error("Meetrondje '%s' already imported" % mr)
208 continue
209 (counters, ap_pool, meting_pool) = process_netstumber(filename)
210 counters = import_accespoints(ap_pool, counters)
211 counters = import_metingen(mr, meting_pool, counters)
212
213 logger.info(("summary metingen : total:%(meting_total)-6s" +
214 "added:%(meting_added)-6s failed:%(meting_failed)-6s" +
215 "ignored:%(meting_ignored)-6s") % counters)
216 else:
217 raise CommandError("file '%s' format not recognized" % filename)
Note: See TracBrowser for help on using the repository browser.