-
Notifications
You must be signed in to change notification settings - Fork 14
/
maxmind.py
154 lines (123 loc) · 5.28 KB
/
maxmind.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
from mlabns.db import model
from mlabns.util import constants
import cloudstorage as gcs
import datetime
import geoip2.database
import geoip2.errors
import logging
import maxminddb
_maxmind_geo_reader = None
class GeoRecord:
def __init__(self, city=None, country=None, latitude=None, longitude=None):
self.city = city
self.country = country
self.latitude = latitude
self.longitude = longitude
def __eq__(self, other):
return ((self.city == other.city) and
(self.country == other.country) and
(self.latitude == other.latitude) and
(self.longitude == other.longitude))
def __ne__(self, other):
return not self.__eq__(other)
def get_database_file():
bucket = '/' + constants.GEOLOCATION_MAXMIND_GCS_BUCKET
bucket_path = bucket + '/' + constants.GEOLOCATION_MAXMIND_BUCKET_PATH
filename = bucket_path + '/' + constants.GEOLOCATION_MAXMIND_CITY_FILE
logging.info('MaxMind database GCS path is: %s', filename)
try:
file_stat = gcs.stat(filename)
database_file = gcs.open(filename)
logging.info('MaxMind database file creation time is: %s',
datetime.datetime.fromtimestamp(
file_stat.st_ctime).strftime('%Y-%m-%d %H:%M:%S'))
except gcs.NotFoundError:
logging.error('MaxMind database file not found in GCS: %s', filename)
raise
return database_file
def get_geo_reader():
global _maxmind_geo_reader
if _maxmind_geo_reader is None:
try:
# The third parameter is for the "mode", which corresponds to
# various integer constants in the MaxMind code. 16 corresponds to
# MODE_FD, which means that we will be passing a file descriptor,
# not a path to the MM database.
maxmind_database_file = get_database_file()
geo_reader = geoip2.database.Reader(maxmind_database_file, None, 16)
except maxminddb.errors.InvalidDatabaseError:
logging.error('Invalid MaxMind database file.')
return GeoRecord()
except IOError:
logging.error('Cannot read MaxMind database file.')
return GeoRecord()
_maxmind_geo_reader = geo_reader
return _maxmind_geo_reader
def get_ip_geolocation(ip_address):
"""Returns the geolocation data associated with an IP address from MaxMind.
Args:
ip_address: A string describing an IP address (v4 or v6).
Returns:
A populated GeoRecord if matching geolocation data is found for the
IP address. Otherwise, an empty GeoRecord.
"""
try:
geo_response = get_geo_reader().city(ip_address)
except ValueError, e:
logging.error('Malformed IP address %s for MaxMind lookup: %s',
ip_address, e)
return GeoRecord()
except geoip2.errors.AddressNotFoundError, e:
logging.error('IP address not found in MaxMind database: %s',
ip_address)
return GeoRecord()
if not geo_response:
logging.error('IP %s not found in the MaxMind database.', ip_address)
return GeoRecord()
return GeoRecord(city=geo_response.city.name,
country=geo_response.country.iso_code,
latitude=geo_response.location.latitude,
longitude=geo_response.location.longitude)
def get_country_geolocation(country, country_table=model.CountryCode):
"""Returns the geolocation data associated with a country code.
Args:
country: A string describing a two alphanumeric country code.
country_table: Datastore table from which to retrieve country
information.
Returns:
A GeoRecord containing the geolocation data if found, otherwise an
empty GeoRecord.
"""
geo_record = GeoRecord()
logging.info('Retrieving geolocation info for country %s.', country)
location = country_table.get_by_key_name(country)
if location is not None:
geo_record.city = constants.UNKNOWN_CITY
geo_record.country = location.alpha2_code
geo_record.latitude = location.latitude
geo_record.longitude = location.longitude
return geo_record
def get_city_geolocation(city, country, city_table=model.MaxmindCityLocation):
"""Returns the geolocation data associated with a city and country code.
Args:
city: A string specifying the name of the city.
country: A string describing a two alphanumeric country code.
city_table: Datastore table from which to retrieve city information.
Returns:
A GeoRecord containing the geolocation data if found, otherwise an empty
GeoRecord.
"""
geo_record = GeoRecord()
logging.info('Retrieving geolocation info for country %s, city %s.', city,
country)
location = city_table.gql('WHERE city = :city AND country = :country',
city=city,
country=country).get()
if location is None:
logging.error('%s, %s not found in the database.', city, country)
return geo_record
geo_record.city = location.city
geo_record.country = location.country
geo_record.latitude = location.latitude
geo_record.longitude = location.longitude
return geo_record