OLD | NEW |
| (Empty) |
1 # Copyright 2015 The Chromium Authors. All rights reserved. | |
2 # Use of this source code is governed by a BSD-style license that can be | |
3 # found in the LICENSE file. | |
4 | |
5 import logging | |
6 import os | |
7 import socket | |
8 | |
9 import infra_libs | |
10 from infra_libs.event_mon.protos.chrome_infra_log_pb2 import ( | |
11 ChromeInfraEvent, ServiceEvent) | |
12 from infra_libs.event_mon import router as ev_router | |
13 | |
14 DEFAULT_SERVICE_ACCOUNT_CREDS = 'service-account-event-mon.json' | |
15 RUNTYPES = set(('dry', 'test', 'prod', 'file')) | |
16 | |
17 # Remote endpoints | |
18 ENDPOINTS = { | |
19 'test': 'https://jmt17.google.com/log', | |
20 'prod': 'https://play.googleapis.com/log', | |
21 } | |
22 | |
23 # Instance of router._Router (singleton) | |
24 _router = None | |
25 | |
26 # Cache some generally useful values / options | |
27 _cache = {} | |
28 | |
29 | |
30 def add_argparse_options(parser): | |
31 # The default values should make sense for local testing, not production. | |
32 group = parser.add_argument_group('Event monitoring (event_mon) ' | |
33 'global options') | |
34 group.add_argument('--dry-run', default=False, | |
35 action='store_true', | |
36 help='When passed, just print what would happen, but ' | |
37 'do not do it.' | |
38 ) | |
39 group.add_argument('--event-mon-run-type', default='dry', | |
40 choices=RUNTYPES, | |
41 help='Determine how to send data. "dry" does not send\n' | |
42 'anything. "test" sends to the test endpoint, \n' | |
43 '"prod" to the actual production endpoint, and "file" \n' | |
44 'writes to a file.') | |
45 group.add_argument('--event-mon-output-file', default='event_mon.output', | |
46 help='File into which LogEventLite serialized protos are\n' | |
47 'written when --event-mon-run-type is "file"') | |
48 group.add_argument('--event-mon-service-name', | |
49 help='Service name to use in log events.') | |
50 group.add_argument('--event-mon-hostname', | |
51 help='Hostname to use in log events.') | |
52 group.add_argument('--event-mon-appengine-name', | |
53 help='App name to use in log events.') | |
54 group.add_argument('--event-mon-service-account-creds', | |
55 default=DEFAULT_SERVICE_ACCOUNT_CREDS, | |
56 metavar='JSON_FILE', | |
57 help="Path to a json file containing a service account's" | |
58 "\ncredentials. This is relative to the path specified\n" | |
59 "in --event-mon-service-accounts-creds-root\n" | |
60 "Defaults to '%(default)s'") | |
61 group.add_argument('--event-mon-service-accounts-creds-root', | |
62 metavar='DIR', | |
63 default=infra_libs.SERVICE_ACCOUNTS_CREDS_ROOT, | |
64 help="Directory containing service accounts credentials.\n" | |
65 "Defaults to %(default)s" | |
66 ) | |
67 group.add_argument('--event-mon-http-timeout', default=10, type=int, | |
68 help='Timeout in seconds for HTTP requests to send events') | |
69 group.add_argument('--event-mon-http-retry-backoff', default=2., type=float, | |
70 help='Time in seconds before retrying POSTing to the HTTP ' | |
71 'endpoint. Randomized exponential backoff is applied on ' | |
72 'subsequent retries.') | |
73 | |
74 | |
75 def process_argparse_options(args): # pragma: no cover | |
76 """Initializes event monitoring based on provided arguments. | |
77 | |
78 Args: | |
79 args(argparse.Namespace): output of ArgumentParser.parse_args. | |
80 """ | |
81 setup_monitoring( | |
82 run_type=args.event_mon_run_type, | |
83 hostname=args.event_mon_hostname, | |
84 service_name=args.event_mon_service_name, | |
85 appengine_name=args.event_mon_appengine_name, | |
86 service_account_creds=args.event_mon_service_account_creds, | |
87 service_accounts_creds_root=args.event_mon_service_accounts_creds_root, | |
88 output_file=args.event_mon_output_file, | |
89 dry_run=args.dry_run, | |
90 http_timeout=args.event_mon_http_timeout, | |
91 http_retry_backoff=args.event_mon_http_retry_backoff | |
92 ) | |
93 | |
94 | |
95 def setup_monitoring(run_type='dry', | |
96 hostname=None, | |
97 service_name=None, | |
98 appengine_name=None, | |
99 service_account_creds=None, | |
100 service_accounts_creds_root=None, | |
101 output_file=None, | |
102 dry_run=False, | |
103 http_timeout=10, | |
104 http_retry_backoff=2.): | |
105 """Initializes event monitoring. | |
106 | |
107 This function is mainly used to provide default global values which are | |
108 required for the module to work. | |
109 | |
110 If you're implementing a command-line tool, use process_argparse_options | |
111 instead. | |
112 | |
113 Args: | |
114 run_type (str): One of 'dry', 'test', or 'prod'. Do respectively nothing, | |
115 hit the testing endpoint and the production endpoint. | |
116 | |
117 hostname (str): hostname as it should appear in the event. If not provided | |
118 a default value is computed. | |
119 | |
120 service_name (str): logical name of the service that emits events. e.g. | |
121 "commit_queue". | |
122 | |
123 appengine_name (str): name of the appengine app, if running on appengine. | |
124 | |
125 service_account_creds (str): path to a json file containing a service | |
126 account's credentials obtained from a Google Cloud project. **Path is | |
127 relative to service_account_creds_root**, which is not the current path by | |
128 default. See infra_libs.authentication for details. | |
129 | |
130 service_account_creds_root (str): path containing credentials files. | |
131 | |
132 output_file (str): file where to write the output in run_type == 'file' | |
133 mode. | |
134 | |
135 dry_run (bool): if True, the code has no side-effect, what would have been | |
136 done is printed instead. | |
137 | |
138 http_timeout (int): timeout in seconds for HTTP requests to send events. | |
139 | |
140 http_retry_backoff (float): time in seconds before retrying POSTing to the | |
141 HTTP endpoint. Randomized exponential backoff is applied on subsequent | |
142 retries. | |
143 """ | |
144 global _router | |
145 logging.debug('event_mon: setting up monitoring.') | |
146 | |
147 if not _router: | |
148 default_event = ChromeInfraEvent() | |
149 | |
150 hostname = hostname or socket.getfqdn() | |
151 # hostname might be empty string or None on some systems, who knows. | |
152 if hostname: # pragma: no branch | |
153 default_event.event_source.host_name = hostname | |
154 else: # pragma: no cover | |
155 logging.warning('event_mon: unable to determine hostname.') | |
156 | |
157 if service_name: | |
158 default_event.event_source.service_name = service_name | |
159 if appengine_name: | |
160 default_event.event_source.appengine_name = appengine_name | |
161 | |
162 _cache['default_event'] = default_event | |
163 if run_type in ('prod', 'test'): | |
164 _cache['service_account_creds'] = service_account_creds | |
165 _cache['service_accounts_creds_root'] = service_accounts_creds_root | |
166 else: | |
167 _cache['service_account_creds'] = None | |
168 _cache['service_accounts_creds_root'] = None | |
169 | |
170 if run_type not in RUNTYPES: | |
171 logging.error('Unknown run_type (%s). Setting to "dry"', run_type) | |
172 run_type = 'dry' | |
173 | |
174 if run_type == 'dry': | |
175 # If we are running on AppEngine or devserver, use logging module. | |
176 server_software = os.environ.get('SERVER_SOFTWARE', '') | |
177 if (server_software.startswith('Google App Engine') or | |
178 server_software.startswith('Development')): | |
179 _router = ev_router._LoggingStreamRouter() | |
180 else: | |
181 _router = ev_router._TextStreamRouter() | |
182 elif run_type == 'file': | |
183 _router = ev_router._LocalFileRouter(output_file, | |
184 dry_run=dry_run) | |
185 else: | |
186 _router = ev_router._HttpRouter(_cache, | |
187 ENDPOINTS.get(run_type), | |
188 dry_run=dry_run, | |
189 timeout=http_timeout, | |
190 retry_backoff=http_retry_backoff) | |
191 | |
192 | |
193 def get_default_event(): | |
194 """Returns a copy of the default event.""" | |
195 | |
196 # We return a copy here to tell people not to modify the event directly. | |
197 ret = ChromeInfraEvent() | |
198 ret.CopyFrom(_cache['default_event']) | |
199 return ret | |
200 | |
201 | |
202 def set_default_event(event): | |
203 """Change the default ChromeInfraEvent used to compute all events. | |
204 | |
205 Args: | |
206 event (ChromeInfraEvent): default event | |
207 """ | |
208 # Here we raise an exception because failing to set the default event | |
209 # could lead to invalid data in the database. | |
210 if not isinstance(event, ChromeInfraEvent): | |
211 msg = ('A ChromeInfraEvent is required as the default event. Got %s' % | |
212 type(event)) | |
213 logging.error(msg) | |
214 raise TypeError(msg) | |
215 | |
216 _cache['default_event'] = event | |
217 | |
218 | |
219 def close(): | |
220 """Reset the state. | |
221 | |
222 Call this right before exiting the program. | |
223 | |
224 Returns: | |
225 success (bool): False if an error occured | |
226 """ | |
227 global _router, _cache | |
228 _router = None | |
229 _cache = {} | |
230 return True | |
OLD | NEW |