├── .gitignore
├── config.yaml.example
├── stylesheets
└── main.css
├── appengine_config.py
├── test.json
├── app.yaml
├── generate_random_data.py
├── index.html
└── phone_home.py
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | index.yaml
3 | config.yaml
4 | boto
--------------------------------------------------------------------------------
/config.yaml.example:
--------------------------------------------------------------------------------
1 | amazon_access_key: XXX
2 | amazon_secret_key: YYY
--------------------------------------------------------------------------------
/stylesheets/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | font-family: Verdana, Helvetica, sans-serif;
3 | background-color: #DDDDDD;
4 | }
--------------------------------------------------------------------------------
/appengine_config.py:
--------------------------------------------------------------------------------
1 | # appengine_config.py
2 | from google.appengine.ext import vendor
3 |
4 | # Add any libraries install in the "lib" folder.
5 | vendor.add('lib')
6 |
7 |
--------------------------------------------------------------------------------
/test.json:
--------------------------------------------------------------------------------
1 | {
2 | "instanceId" : "i-48b5bb65",
3 | "billingProducts" : null,
4 | "imageId" : "ami-e2dc278a",
5 | "architecture" : "x86_64",
6 | "pendingTime" : "2014-08-20T17:22:21Z",
7 | "instanceType" : "t1.micro",
8 | "accountId" : "555219204010",
9 | "kernelId" : "aki-0b4aa462",
10 | "ramdiskId" : null,
11 | "region" : "us-east-1",
12 | "version" : "2010-08-31",
13 | "availabilityZone" : "us-east-1b",
14 | "privateIp" : "10.178.172.95",
15 | "devpayProductCodes" : null
16 | }
--------------------------------------------------------------------------------
/app.yaml:
--------------------------------------------------------------------------------
1 | runtime: python27
2 | api_version: 1
3 | threadsafe: true
4 |
5 | handlers:
6 |
7 | # - url: /admin/.*
8 | # script: $PYTHON_LIB/google/appengine/ext/admin
9 | # login: admin
10 |
11 | - url: /stylesheets
12 | static_dir: stylesheets
13 |
14 | - url: /.*
15 | script: phone_home.application
16 |
17 | # [START libraries]
18 | libraries:
19 | - name: webapp2
20 | version: latest
21 | - name: jinja2
22 | version: latest
23 | # - name: ssl
24 | # version: "latest"
25 | # - name: boto3
26 | # version: "latest"
27 | # [END libraries]
28 |
--------------------------------------------------------------------------------
/generate_random_data.py:
--------------------------------------------------------------------------------
1 | # DO NOT RUN THIS IN PRODUCTION! It's for testing only
2 |
3 | import os
4 | import pprint
5 | import random
6 | import time
7 | import datetime
8 | import string
9 | import hashlib
10 |
11 | from google.appengine.api import memcache
12 | from google.appengine.api import mail
13 | from google.appengine.api import urlfetch
14 | from google.appengine.ext import db
15 |
16 | #pprint.pprint(os.environ.copy())
17 |
18 | from phone_home import AMILaunch, get_parent
19 |
20 | def randstring(n):
21 | return ''.join(random.choice(string.ascii_uppercase + string.digits) for _ in range(n))
22 |
23 |
24 | def random_date(start_time_string, end_time_string, format_string, random_number):
25 | """
26 | Get a time at a proportion of a range of two formatted times.
27 | start and end should be strings specifying times formated in the
28 | given format (strftime-style), giving an interval [start, end].
29 | prop specifies how a proportion of the interval to be taken after
30 | start. The returned time will be in the specified format.
31 | """
32 | dt_start = datetime.datetime.strptime(start_time_string, format_string)
33 | dt_end = datetime.datetime.strptime(end_time_string, format_string)
34 |
35 | start_time = time.mktime(dt_start.timetuple()) + dt_start.microsecond / 1000000.0
36 | end_time = time.mktime(dt_end.timetuple()) + dt_end.microsecond / 1000000.0
37 |
38 | random_time = start_time + random_number * (end_time - start_time)
39 |
40 | ret = datetime.datetime.fromtimestamp(random_time)#.strftime(format_string)
41 | # print(ret.__class__.__name__)
42 | return ret
43 |
44 |
45 |
46 | for i in range(200):
47 | ami_launch = AMILaunch(parent=get_parent())
48 | account_id = randstring(12)
49 | ami_launch.is_bioc_account = bool(random.getrandbits(1))
50 | if ami_launch.is_bioc_account:
51 | account_id = "some consistent string"
52 | ami_launch.account_hash = hashlib.md5(account_id.encode()).hexdigest()
53 | ami_launch.ami_id = "ami-" + randstring(8)
54 | ami_launch.instance_type = randstring(5)
55 | ami_launch.region = randstring(6)
56 | ami_launch.availability_zone = randstring(9)
57 | ami_launch.ami_name = randstring(20)
58 | ami_launch.ami_description = randstring(30)
59 | ami_launch.bioc_version = randstring(3)
60 | #print(ami_launch)
61 | ami_launch.date = random_date("2000/01/01 00:00:00.000000", "2049/12/31 23:59:59.999999", '%Y/%m/%d %H:%M:%S.%f', random.random())
62 | #print(ami_launch.date)
63 | ami_launch.put()
64 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 | {% autoescape true %}
3 |
4 |
5 | Bioconductor AMI Phone Home
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
66 |
67 |
68 |
69 | ami phone home
70 |
71 |
72 |
73 |
74 | | ami-id |
75 | bioc version |
76 | ami name |
77 | instance type |
78 | region |
79 | availability zone |
80 | is bioc account |
81 | date |
82 | account hash |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | {% endautoescape %}
97 |
--------------------------------------------------------------------------------
/phone_home.py:
--------------------------------------------------------------------------------
1 | # [START imports]
2 | import os
3 | import urllib
4 | import hashlib
5 | import sys
6 |
7 | from google.appengine.api import users
8 | from google.appengine.ext import ndb
9 | from google.appengine.datastore.datastore_query import Cursor
10 | from webapp2_extras import json
11 |
12 | import jinja2
13 | import webapp2
14 | from webapp2_extras import json
15 | import yaml
16 | import urllib2
17 | import boto.ec2
18 |
19 | JINJA_ENVIRONMENT = jinja2.Environment(
20 | loader=jinja2.FileSystemLoader(os.path.dirname(__file__)),
21 | extensions=['jinja2.ext.autoescape'],
22 | autoescape=True)
23 | # [END imports]
24 |
25 | def get_parent():
26 | return ndb.Key('phone-home Parent', 'phone-home Parent name')
27 |
28 | def get_bioc_version(ami_id):
29 | response = urllib2.urlopen('https://raw.githubusercontent.com/Bioconductor/bioconductor.org/master/config.yaml')
30 | obj = yaml.load(response)
31 | for k,v in obj['ami_ids'].iteritems():
32 | if v == ami_id:
33 | return k.replace("bioc", "").replace("_", ".")
34 | return None
35 |
36 | def get_ami_info(ami_id):
37 | configfile = os.path.join(os.path.dirname(__file__), "config.yaml")
38 | stream = open(configfile, 'r')
39 | config = yaml.load(stream)
40 | conn = boto.ec2.connect_to_region("us-east-1",
41 | aws_access_key_id=config['amazon_access_key'],
42 | aws_secret_access_key=config['amazon_secret_key'],
43 | validate_certs=False)
44 | try:
45 | img = conn.get_all_images([ami_id])
46 | return({'name': img[0].name, 'description': img[0].description})
47 | except:
48 | return None
49 |
50 | class AMILaunch(ndb.Model):
51 | """Models a launch of a BioC AMI"""
52 | is_bioc_account = ndb.BooleanProperty()
53 | account_hash = ndb.StringProperty()
54 | is_aws_ip = ndb.BooleanProperty()
55 | ami_id = ndb.StringProperty()
56 | bioc_version = ndb.StringProperty()
57 | ami_name = ndb.StringProperty()
58 | ami_description = ndb.StringProperty()
59 | instance_type = ndb.StringProperty()
60 | region = ndb.StringProperty()
61 | date = ndb.DateTimeProperty(auto_now_add=True)
62 | # date = ndb.DateTimeProperty() # only needed when populating synthetic data
63 | availability_zone = ndb.StringProperty()
64 |
65 | class FrontPage(webapp2.RequestHandler):
66 | def get(self):
67 | return webapp2.redirect('/phone-home')
68 |
69 | class TableData(webapp2.RequestHandler):
70 | def post(self):
71 | in_obj = self.request.POST
72 |
73 | out_obj = {}
74 | out_obj['draw'] = int(in_obj['draw'])
75 |
76 | self.response.content_type = 'application/json'
77 |
78 | count = 99999999 # FIXME if real number of records exceeds this, increment this!!
79 | out_obj['recordsTotal'] = count
80 | out_obj['recordsFiltered'] = count # OK?
81 |
82 | start = in_obj['start'] # starts at 0
83 | pagesize = int(in_obj['length']) # number of rows to return
84 |
85 | cursor = None
86 | if len(in_obj['next_cursor']):
87 | cursor = Cursor(urlsafe=in_obj['next_cursor'])
88 | print("cursor is")
89 | if cursor:
90 | print(cursor.urlsafe())
91 | else:
92 | print("(none)")
93 |
94 |
95 | query = AMILaunch.query(ancestor=get_parent()).order(-AMILaunch.date)
96 | rows, next_cursor, more = query.fetch_page(pagesize, start_cursor=cursor)
97 |
98 | if next_cursor:
99 | print("urlsafe is")
100 | print(next_cursor.urlsafe())
101 |
102 | out_obj['next_cursor'] = next_cursor.urlsafe()
103 | else:
104 | out_obj['next_cursor'] = None
105 | print("urlsafe is None")
106 |
107 | data = []
108 |
109 | for row in rows:
110 | data.append([row.ami_id, row.bioc_version, row.ami_name,
111 | row.instance_type, row.region, row.availability_zone,
112 | row.is_bioc_account, str(row.date), row.account_hash])
113 |
114 | out_obj['data'] = data
115 | self.response.write(json.encode(out_obj))
116 |
117 | class AWSHandler(webapp2.RequestHandler):
118 | def get(self):
119 | template = JINJA_ENVIRONMENT.get_template('index.html')
120 | self.response.write(template.render())
121 |
122 | def post(self):
123 | # remote_addr shows up as "::1" when calling from localhost
124 | # or is it when using the simulated version of the
125 | # GAE environment? not sure.
126 | # print("remote ip is %s" % self.request.remote_addr)
127 | raw = self.request.body
128 | try:
129 | obj = json.decode(raw)
130 | except ValueError:
131 | self.response.out.write("invalid json")
132 | return
133 |
134 |
135 | ami_launch = AMILaunch(parent=get_parent())
136 | ami_launch.is_bioc_account = obj['accountId'] == "555219204010"
137 | if not ami_launch.is_bioc_account:
138 | ami_launch.account_hash = hashlib.md5(obj['accountId'].encode()).hexdigest()
139 | ami_launch.ami_id = obj['imageId']
140 | ami_launch.instance_type = obj['instanceType']
141 | ami_launch.region = obj['region']
142 | ami_launch.availability_zone = obj['availabilityZone']
143 |
144 | is_aws_ip("foo")
145 |
146 | ami_launch.bioc_version = get_bioc_version(obj['imageId'])
147 |
148 | ami_info = get_ami_info(obj['imageId'])
149 | if ami_info is not None:
150 | ami_launch.ami_name = ami_info['name']
151 | ami_launch.ami_description = ami_info['description']
152 |
153 | ami_launch.put()
154 |
155 | self.response.out.write("thanx\n")
156 |
157 |
158 |
159 |
160 | application = webapp2.WSGIApplication([
161 | ('/phone-home', AWSHandler),
162 | ('/', FrontPage),
163 | ('/table-data', TableData),
164 | ], debug=True)
165 |
166 | def is_aws_ip(ip):
167 | aws_ips="""72.44.32.0/19 (72.44.32.0 - 72.44.63.255)
168 | 67.202.0.0/18 (67.202.0.0 - 67.202.63.255)
169 | 75.101.128.0/17 (75.101.128.0 - 75.101.255.255)
170 | 174.129.0.0/16 (174.129.0.0 - 174.129.255.255)
171 | 204.236.192.0/18 (204.236.192.0 - 204.236.255.255)
172 | 184.73.0.0/16 (184.73.0.0 - 184.73.255.255)
173 | 184.72.128.0/17 (184.72.128.0 - 184.72.255.255)
174 | 184.72.64.0/18 (184.72.64.0 - 184.72.127.255)
175 | 50.16.0.0/15 (50.16.0.0 - 50.17.255.255)
176 | 50.19.0.0/16 (50.19.0.0 - 50.19.255.255)
177 | 107.20.0.0/14 (107.20.0.0 - 107.23.255.255)
178 | 23.20.0.0/14 (23.20.0.0 - 23.23.255.255)
179 | 54.242.0.0/15 (54.242.0.0 - 54.243.255.255)
180 | 54.234.0.0/15 (54.234.0.0 - 54.235.255.255)
181 | 54.236.0.0/15 (54.236.0.0 - 54.237.255.255)
182 | 54.224.0.0/15 (54.224.0.0 - 54.225.255.255)
183 | 54.226.0.0/15 (54.226.0.0 - 54.227.255.255)
184 | 54.208.0.0/15 (54.208.0.0 - 54.209.255.255)
185 | 54.210.0.0/15 (54.210.0.0 - 54.211.255.255)
186 | 54.221.0.0/16 (54.221.0.0 - 54.221.255.255)
187 | 54.204.0.0/15 (54.204.0.0 - 54.205.255.255)
188 | 54.196.0.0/15 (54.196.0.0 - 54.197.255.255)
189 | 54.198.0.0/16 (54.198.0.0 - 54.198.255.255)
190 | 54.80.0.0/13 (54.80.0.0 - 54.87.255.255)
191 | 54.88.0.0/14 (54.88.0.0 - 54.91.255.255)
192 | 54.92.0.0/16 (54.92.0.0 - 54.92.255.255)
193 | 54.92.128.0/17 (54.92.128.0 - 54.92.255.255)
194 | 54.160.0.0/13 (54.160.0.0 - 54.167.255.255)
195 | 54.172.0.0/15 (54.172.0.0 - 54.173.255.255)
196 | 50.112.0.0/16 (50.112.0.0 - 50.112.255.255)
197 | 54.245.0.0/16 (54.245.0.0 - 54.245.255.255)
198 | 54.244.0.0/16 (54.244.0.0 - 54.244.255.255)
199 | 54.214.0.0/16 (54.214.0.0 - 54.214.255.255)
200 | 54.212.0.0/15 (54.212.0.0 - 54.213.255.255)
201 | 54.218.0.0/16 (54.218.0.0 - 54.218.255.255)
202 | 54.200.0.0/15 (54.200.0.0 - 54.201.255.255)
203 | 54.202.0.0/15 (54.202.0.0 - 54.203.255.255)
204 | 54.184.0.0/13 (54.184.0.0 - 54.191.255.255)
205 | 54.68.0.0/14 (54.68.0.0 - 54.71.255.255)
206 | 204.236.128.0/18 (204.236.128.0 - 204.236.191.255)
207 | 184.72.0.0/18 (184.72.0.0 - 184.72.63.255)
208 | 50.18.0.0/16 (50.18.0.0 - 50.18.255.255)
209 | 184.169.128.0/17 (184.169.128.0 - 184.169.255.255)
210 | 54.241.0.0/16 (54.241.0.0 - 54.241.255.255)
211 | 54.215.0.0/16 (54.215.0.0 - 54.215.255.255)
212 | 54.219.0.0/16 (54.219.0.0 - 54.219.255.255)
213 | 54.193.0.0/16 (54.193.0.0 - 54.193.255.255)
214 | 54.176.0.0/15 (54.176.0.0 - 54.177.255.255)
215 | 54.183.0.0/16 (54.183.0.0 - 54.183.255.255)
216 | 54.67.0.0/16 (54.67.0.0 - 54.67.255.255) NEW
217 | 79.125.0.0/17 (79.125.0.0 - 79.125.127.255)
218 | 46.51.128.0/18 (46.51.128.0 - 46.51.191.255)
219 | 46.51.192.0/20 (46.51.192.0 - 46.51.207.255)
220 | 46.137.0.0/17 (46.137.0.0 - 46.137.127.255)
221 | 46.137.128.0/18 (46.137.128.0 - 46.137.191.255)
222 | 176.34.128.0/17 (176.34.128.0 - 176.34.255.255)
223 | 176.34.64.0/18 (176.34.64.0 - 176.34.127.255)
224 | 54.247.0.0/16 (54.247.0.0 - 54.247.255.255)
225 | 54.246.0.0/16 (54.246.0.0 - 54.246.255.255)
226 | 54.228.0.0/16 (54.228.0.0 - 54.228.255.255)
227 | 54.216.0.0/15 (54.216.0.0 - 54.217.255.255)
228 | 54.229.0.0/16 (54.229.0.0 - 54.229.255.255)
229 | 54.220.0.0/16 (54.220.0.0 - 54.220.255.255)
230 | 54.194.0.0/15 (54.194.0.0 - 54.195.255.255)
231 | 54.72.0.0/14 (54.72.0.0 - 54.75.255.255)
232 | 54.76.0.0/15 (54.76.0.0 - 54.77.255.255)
233 | 54.78.0.0/16 (54.78.0.0 - 54.78.255.255)
234 | 54.74.0.0/15 (54.74.0.0 - 54.75.255.255)
235 | 185.48.120.0/22 (185.48.120.0 - 185.48.123.255)
236 | 54.170.0.0/15 (54.170.0.0 - 54.171.255.255)
237 | 175.41.128.0/18 (175.41.128.0 - 175.41.191.255)
238 | 122.248.192.0/18 (122.248.192.0 - 122.248.255.255)
239 | 46.137.192.0/18 (46.137.192.0 - 46.137.255.255)
240 | 46.51.216.0/21 (46.51.216.0 - 46.51.223.255)
241 | 54.251.0.0/16 (54.251.0.0 - 54.251.255.255)
242 | 54.254.0.0/16 (54.254.0.0 - 54.254.255.255)
243 | 54.255.0.0/16 (54.255.0.0 - 54.255.255.255)
244 | 54.179.0.0/16 (54.179.0.0 - 54.179.255.255)
245 | 54.169.0.0/16 (54.169.0.0 - 54.169.255.255)
246 | 54.252.0.0/16 (54.252.0.0 - 54.252.255.255)
247 | 54.253.0.0/16 (54.253.0.0 - 54.253.255.255)
248 | 54.206.0.0/16 (54.206.0.0 - 54.206.255.255)
249 | 54.79.0.0/16 (54.79.0.0 - 54.79.255.255)
250 | 54.66.0.0/16 (54.66.0.0 - 54.66.255.255)
251 | 175.41.192.0/18 (175.41.192.0 - 175.41.255.255)
252 | 46.51.224.0/19 (46.51.224.0 - 46.51.255.255)
253 | 176.32.64.0/19 (176.32.64.0 - 176.32.95.255)
254 | 103.4.8.0/21 (103.4.8.0 - 103.4.15.255)
255 | 176.34.0.0/18 (176.34.0.0 - 176.34.63.255)
256 | 54.248.0.0/15 (54.248.0.0 - 54.249.255.255)
257 | 54.250.0.0/16 (54.250.0.0 - 54.250.255.255)
258 | 54.238.0.0/16 (54.238.0.0 - 54.238.255.255)
259 | 54.199.0.0/16 (54.199.0.0 - 54.199.255.255)
260 | 54.178.0.0/16 (54.178.0.0 - 54.178.255.255)
261 | 54.95.0.0/16 (54.95.0.0-54.95.255.255)
262 | 54.92.0.0/17 (54.92.0.0 - 54.92.127.255)
263 | 54.168.0.0/16 (54.168.0.0 - 54.168.255.255)
264 | 54.64.0.0/15 (54.64.0.0 - 54.65.255.255)
265 | 177.71.128.0/17 (177.71.128.0 - 177.71.255.255)
266 | 54.232.0.0/16 (54.232.0.0 - 54.232.255.255)
267 | 54.233.0.0/18 (54.233.0.0 - 54.233.63.255)
268 | 54.207.0.0/16 (54.207.0.0 - 54.207.255.255)
269 | 54.94.0.0/16 (54.94.0.0 - 54.94.255.255)
270 | 54.223.0.0/16 (54.223.0.0 - 54.223.255.255)
271 | 96.127.0.0/18 (96.127.0.0 - 96.127.63.255)
272 | """
273 | ip_segs = ip.split(".")
274 |
--------------------------------------------------------------------------------