)]}'
{"designate/api/v2/controllers/rest.py":[{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":62,"context_line":""},{"line_number":63,"context_line":"    @property"},{"line_number":64,"context_line":"    def worker_api(self):"},{"line_number":65,"context_line":"        return worker_rpcapi.WorkerAPI.get_instance()"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":"    def _apply_filter_params(self, params, accepted_filters, criterion):"},{"line_number":68,"context_line":"        invalid\u003d[]"}],"source_content_type":"text/x-python","patch_set":2,"id":"9aed3d3a_480f3c2e","line":65,"updated":"2016-03-01 16:30:35.000000000","message":"Want to make this lazy?","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"b7e001e669080ceb0c7060ce3fb84c54dd238071","unresolved":false,"context_lines":[{"line_number":62,"context_line":""},{"line_number":63,"context_line":"    @property"},{"line_number":64,"context_line":"    def worker_api(self):"},{"line_number":65,"context_line":"        return worker_rpcapi.WorkerAPI.get_instance()"},{"line_number":66,"context_line":""},{"line_number":67,"context_line":"    def _apply_filter_params(self, params, accepted_filters, criterion):"},{"line_number":68,"context_line":"        invalid\u003d[]"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_29b678dc","line":65,"in_reply_to":"9aed3d3a_480f3c2e","updated":"2016-03-01 17:04:36.000000000","message":"Yep, need to rebase/lazy all the things","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"}],"designate/producer/__init__.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"cd43cb95f306b55e264e2d5daaeabdc23ad6ec26","unresolved":false,"context_lines":[{"line_number":23,"context_line":""},{"line_number":24,"context_line":"OPTS \u003d ["},{"line_number":25,"context_line":"    cfg.IntOpt(\u0027workers\u0027,"},{"line_number":26,"context_line":"               help\u003d\u0027Number of Zone Manager worker processes to spawn\u0027),"},{"line_number":27,"context_line":"    cfg.IntOpt(\u0027threads\u0027, default\u003d1000,"},{"line_number":28,"context_line":"               help\u003d\u0027Number of Zone Manager greenthreads to spawn\u0027),"},{"line_number":29,"context_line":"    cfg.ListOpt(\u0027enabled_tasks\u0027,"}],"source_content_type":"text/x-python","patch_set":31,"id":"dada55a8_8774b9cf","line":26,"range":{"start_line":26,"start_character":31,"end_line":26,"end_character":43},"updated":"2016-07-19 20:47:19.000000000","message":"\"Producer\"","commit_id":"29a6fa98d504f587e2a56ec56101589cdba0032a"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"cd43cb95f306b55e264e2d5daaeabdc23ad6ec26","unresolved":false,"context_lines":[{"line_number":25,"context_line":"    cfg.IntOpt(\u0027workers\u0027,"},{"line_number":26,"context_line":"               help\u003d\u0027Number of Zone Manager worker processes to spawn\u0027),"},{"line_number":27,"context_line":"    cfg.IntOpt(\u0027threads\u0027, default\u003d1000,"},{"line_number":28,"context_line":"               help\u003d\u0027Number of Zone Manager greenthreads to spawn\u0027),"},{"line_number":29,"context_line":"    cfg.ListOpt(\u0027enabled_tasks\u0027,"},{"line_number":30,"context_line":"                help\u003d\u0027Enabled tasks to run\u0027),"},{"line_number":31,"context_line":"    cfg.StrOpt(\u0027storage-driver\u0027, default\u003d\u0027sqlalchemy\u0027,"}],"source_content_type":"text/x-python","patch_set":31,"id":"dada55a8_074f2993","line":28,"range":{"start_line":28,"start_character":31,"end_line":28,"end_character":43},"updated":"2016-07-19 20:47:19.000000000","message":"\"Producer\"","commit_id":"29a6fa98d504f587e2a56ec56101589cdba0032a"}],"designate/producer/tasks.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"cd43cb95f306b55e264e2d5daaeabdc23ad6ec26","unresolved":false,"context_lines":[{"line_number":215,"context_line":""},{"line_number":216,"context_line":"    def __call__(self):"},{"line_number":217,"context_line":"        pstart, pend \u003d self._my_range()"},{"line_number":218,"context_line":"        msg \u003d _LI(\"Refreshing zones between for %(start)s to %(end)s\")"},{"line_number":219,"context_line":"        LOG.info(msg, {\"start\": pstart, \"end\": pend})"},{"line_number":220,"context_line":""},{"line_number":221,"context_line":"        ctxt \u003d context.DesignateContext.get_admin_context()"}],"source_content_type":"text/x-python","patch_set":31,"id":"dada55a8_021ef762","line":218,"range":{"start_line":218,"start_character":36,"end_line":218,"end_character":47},"updated":"2016-07-19 20:47:19.000000000","message":"not part of this patch, but a similar grammatical error","commit_id":"29a6fa98d504f587e2a56ec56101589cdba0032a"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"cd43cb95f306b55e264e2d5daaeabdc23ad6ec26","unresolved":false,"context_lines":[{"line_number":321,"context_line":""},{"line_number":322,"context_line":"    def __call__(self):"},{"line_number":323,"context_line":"        pstart, pend \u003d self._my_range()"},{"line_number":324,"context_line":"        msg \u003d _LI(\"Recovering zones between for %(start)s to %(end)s\")"},{"line_number":325,"context_line":"        LOG.info(msg, {\"start\": pstart, \"end\": pend})"},{"line_number":326,"context_line":""},{"line_number":327,"context_line":"        ctxt \u003d context.DesignateContext.get_admin_context()"}],"source_content_type":"text/x-python","patch_set":31,"id":"dada55a8_a70c5d43","line":324,"range":{"start_line":324,"start_character":36,"end_line":324,"end_character":47},"updated":"2016-07-19 20:47:19.000000000","message":"\"for shards\"","commit_id":"29a6fa98d504f587e2a56ec56101589cdba0032a"}],"designate/tests/test_workers/test_base_task.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e4f2694cc4b9f3d2052ea55fd1f02b5a499fb282","unresolved":false,"context_lines":[{"line_number":12,"context_line":"# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"# License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"# under the License.mport threading"},{"line_number":16,"context_line":"from unittest import TestCase"},{"line_number":17,"context_line":"from mock import Mock"},{"line_number":18,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_ae99597b","line":15,"range":{"start_line":15,"start_character":20,"end_line":15,"end_character":35},"updated":"2016-04-29 16:40:59.000000000","message":"this","commit_id":"924963512198eb967f65208060908af146a293b0"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e6b804d360c7506c42a5ae78936b0ebf47ae4b2d","unresolved":false,"context_lines":[{"line_number":12,"context_line":"# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"# License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"# under the License.mport threading"},{"line_number":16,"context_line":"from unittest import TestCase"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from designate.worker.tasks import base"}],"source_content_type":"text/x-python","patch_set":13,"id":"bab6814e_5ad96cf2","line":15,"range":{"start_line":15,"start_character":20,"end_line":15,"end_character":35},"updated":"2016-05-26 20:26:29.000000000","message":"oh, and this","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"}],"designate/tests/test_workers/test_processing.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e4f2694cc4b9f3d2052ea55fd1f02b5a499fb282","unresolved":false,"context_lines":[{"line_number":12,"context_line":"# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"# License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"# under the License.mport threading"},{"line_number":16,"context_line":"from unittest import TestCase"},{"line_number":17,"context_line":"from mock import Mock"},{"line_number":18,"context_line":""}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_aec2f950","line":15,"range":{"start_line":15,"start_character":20,"end_line":15,"end_character":35},"updated":"2016-04-29 16:40:59.000000000","message":"this","commit_id":"924963512198eb967f65208060908af146a293b0"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e6b804d360c7506c42a5ae78936b0ebf47ae4b2d","unresolved":false,"context_lines":[{"line_number":12,"context_line":"# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"# License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"# under the License.mport threading"},{"line_number":16,"context_line":"from unittest import TestCase"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"from designate.worker import processing"}],"source_content_type":"text/x-python","patch_set":13,"id":"bab6814e_daf39c71","line":15,"range":{"start_line":15,"start_character":20,"end_line":15,"end_character":35},"updated":"2016-05-26 20:26:29.000000000","message":"and this","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"}],"designate/tests/test_workers/test_service.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e6b804d360c7506c42a5ae78936b0ebf47ae4b2d","unresolved":false,"context_lines":[{"line_number":12,"context_line":"# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"# License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"# under the License.mport threading"},{"line_number":16,"context_line":"from unittest import TestCase"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"import mock"}],"source_content_type":"text/x-python","patch_set":13,"id":"bab6814e_baf97850","line":15,"range":{"start_line":15,"start_character":20,"end_line":15,"end_character":35},"updated":"2016-05-26 20:26:29.000000000","message":"^^","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"}],"designate/tests/test_workers/test_zone_tasks.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e4f2694cc4b9f3d2052ea55fd1f02b5a499fb282","unresolved":false,"context_lines":[{"line_number":12,"context_line":"# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"# License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"# under the License.mport threading"},{"line_number":16,"context_line":"from unittest import TestCase"},{"line_number":17,"context_line":"from mock import Mock, patch"},{"line_number":18,"context_line":"import testtools"}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_4ea8858c","line":15,"range":{"start_line":15,"start_character":20,"end_line":15,"end_character":35},"updated":"2016-04-29 16:40:59.000000000","message":"this too","commit_id":"924963512198eb967f65208060908af146a293b0"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e6b804d360c7506c42a5ae78936b0ebf47ae4b2d","unresolved":false,"context_lines":[{"line_number":12,"context_line":"# distributed under the License is distributed on an \"AS IS\" BASIS, WITHOUT"},{"line_number":13,"context_line":"# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the"},{"line_number":14,"context_line":"# License for the specific language governing permissions and limitations"},{"line_number":15,"context_line":"# under the License.mport threading"},{"line_number":16,"context_line":"from unittest import TestCase"},{"line_number":17,"context_line":""},{"line_number":18,"context_line":"import mock"}],"source_content_type":"text/x-python","patch_set":13,"id":"bab6814e_3a9c680d","line":15,"range":{"start_line":15,"start_character":20,"end_line":15,"end_character":35},"updated":"2016-05-26 20:26:29.000000000","message":"^^","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"91aef228eede71ab343564c9908eb438853fb2cb","unresolved":false,"context_lines":[{"line_number":311,"context_line":"        assert success"},{"line_number":312,"context_line":"        assert status \u003d\u003d \u0027SUCCESS\u0027"},{"line_number":313,"context_line":""},{"line_number":314,"context_line":"    def test_threshold_met_false_no_zones(self):"},{"line_number":315,"context_line":"        # Change is looking for serial 2"},{"line_number":316,"context_line":"        # 4 positives, 4 no zones"},{"line_number":317,"context_line":"        ns_results \u003d [2 for i in range(4)] + [0 for i in range(4)]"}],"source_content_type":"text/x-python","patch_set":17,"id":"9abb7d3a_e0aac284","line":314,"updated":"2016-06-02 21:31:36.000000000","message":"Here.","commit_id":"99c52578479b4964b10a84cbc2e6763d3d38fd07"}],"designate/worker/__init__.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":49,"context_line":"                    \u0027to a server\u0027),"},{"line_number":50,"context_line":"    cfg.BoolOpt(\u0027notify\u0027, default\u003dFalse,"},{"line_number":51,"context_line":"                help\u003d\u0027Whether to allow worker to send NOTIFYs, this will \u0027"},{"line_number":52,"context_line":"                     \u0027NOTIFYs in mdns\u0027),"},{"line_number":53,"context_line":"]"},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"CONF.register_opts(OPTS, group\u003d\u0027service:worker\u0027)"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_93fc2c40","line":52,"updated":"2016-02-25 20:33:10.000000000","message":"missing word: \"this will NOTIFYs in mdns\"","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Copyright 2016 Rackspace Inc."},{"line_number":2,"context_line":"#"},{"line_number":3,"context_line":"# Author: Tim Simmons \u003ctim.r.simmons@gmail.com\u003e"},{"line_number":4,"context_line":"#"},{"line_number":5,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":6,"context_line":"# not use this file except in compliance with the License. You may obtain"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_f34be157","line":3,"updated":"2016-03-01 16:30:35.000000000","message":"I noticed this changed within the patchset. This tells me one of two thing. \n\n  1. You have some slick thingamajig to use your .gitconfig that I want to have.\n  2. You are actually two people\n\nBoth are revelations. I\u0027m excited to find out which is correct ;)","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"b7e001e669080ceb0c7060ce3fb84c54dd238071","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Copyright 2016 Rackspace Inc."},{"line_number":2,"context_line":"#"},{"line_number":3,"context_line":"# Author: Tim Simmons \u003ctim.r.simmons@gmail.com\u003e"},{"line_number":4,"context_line":"#"},{"line_number":5,"context_line":"# Licensed under the Apache License, Version 2.0 (the \"License\"); you may"},{"line_number":6,"context_line":"# not use this file except in compliance with the License. You may obtain"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_49030474","line":3,"in_reply_to":"7af24918_f34be157","updated":"2016-03-01 17:04:36.000000000","message":"3. Tim was working on a plane on little sleep and put the wrong email address into his copyright headers.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":26,"context_line":"                help\u003d\u0027Whether to send events to worker instead of \u0027"},{"line_number":27,"context_line":"                     \u0027Pool Manager\u0027),"},{"line_number":28,"context_line":"    cfg.IntOpt(\u0027workers\u0027,"},{"line_number":29,"context_line":"               help\u003d\u0027Number of Zone Manager worker processes to spawn\u0027),"},{"line_number":30,"context_line":"    cfg.IntOpt(\u0027threads\u0027, default\u003d1000,"},{"line_number":31,"context_line":"               help\u003d\u0027Number of Zone Manager greenthreads to spawn\u0027),"},{"line_number":32,"context_line":"    # cfg.ListOpt(\u0027enabled_tasks\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_d3bb450d","line":29,"updated":"2016-03-01 16:30:35.000000000","message":"Hey wait a sec! This isn\u0027t the zone manager!","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    cfg.IntOpt(\u0027workers\u0027,"},{"line_number":29,"context_line":"               help\u003d\u0027Number of Zone Manager worker processes to spawn\u0027),"},{"line_number":30,"context_line":"    cfg.IntOpt(\u0027threads\u0027, default\u003d1000,"},{"line_number":31,"context_line":"               help\u003d\u0027Number of Zone Manager greenthreads to spawn\u0027),"},{"line_number":32,"context_line":"    # cfg.ListOpt(\u0027enabled_tasks\u0027,"},{"line_number":33,"context_line":"    #             help\u003d\u0027Enabled tasks to run\u0027),"},{"line_number":34,"context_line":"    cfg.StrOpt(\u0027storage-driver\u0027, default\u003d\u0027sqlalchemy\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_335ee9e9","line":31,"updated":"2016-03-01 16:30:35.000000000","message":"Wat! Zone Manager!","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"b7e001e669080ceb0c7060ce3fb84c54dd238071","unresolved":false,"context_lines":[{"line_number":28,"context_line":"    cfg.IntOpt(\u0027workers\u0027,"},{"line_number":29,"context_line":"               help\u003d\u0027Number of Zone Manager worker processes to spawn\u0027),"},{"line_number":30,"context_line":"    cfg.IntOpt(\u0027threads\u0027, default\u003d1000,"},{"line_number":31,"context_line":"               help\u003d\u0027Number of Zone Manager greenthreads to spawn\u0027),"},{"line_number":32,"context_line":"    # cfg.ListOpt(\u0027enabled_tasks\u0027,"},{"line_number":33,"context_line":"    #             help\u003d\u0027Enabled tasks to run\u0027),"},{"line_number":34,"context_line":"    cfg.StrOpt(\u0027storage-driver\u0027, default\u003d\u0027sqlalchemy\u0027,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_e9f2d099","line":31,"in_reply_to":"7af24918_335ee9e9","updated":"2016-03-01 17:04:36.000000000","message":"derp","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":30,"context_line":"    cfg.IntOpt(\u0027threads\u0027, default\u003d1000,"},{"line_number":31,"context_line":"               help\u003d\u0027Number of Zone Manager greenthreads to spawn\u0027),"},{"line_number":32,"context_line":"    # cfg.ListOpt(\u0027enabled_tasks\u0027,"},{"line_number":33,"context_line":"    #             help\u003d\u0027Enabled tasks to run\u0027),"},{"line_number":34,"context_line":"    cfg.StrOpt(\u0027storage-driver\u0027, default\u003d\u0027sqlalchemy\u0027,"},{"line_number":35,"context_line":"               help\u003d\u0027The storage driver to use\u0027),"},{"line_number":36,"context_line":"    cfg.IntOpt(\u0027threshold-percentage\u0027, default\u003d100,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_737cf172","line":33,"updated":"2016-03-01 16:30:35.000000000","message":"Hmm... I thought workers did jobs, not tasks.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"}],"designate/worker/processing.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"dbe120db8d2de0ef0f45ebeaf91c4160d5c350ea","unresolved":false,"context_lines":[{"line_number":27,"context_line":""},{"line_number":28,"context_line":""},{"line_number":29,"context_line":"def default_executor():"},{"line_number":30,"context_line":"    thread_count \u003d 5"},{"line_number":31,"context_line":"    try:"},{"line_number":32,"context_line":"        thread_count \u003d CONF[\u0027service:worker\u0027].threads"},{"line_number":33,"context_line":"    except Exception:"},{"line_number":34,"context_line":"        pass"},{"line_number":35,"context_line":""},{"line_number":36,"context_line":"    return futures.ThreadPoolExecutor(thread_count)"},{"line_number":37,"context_line":""}],"source_content_type":"text/x-python","patch_set":13,"id":"dab17558_f60295b3","line":34,"range":{"start_line":30,"start_character":0,"end_line":34,"end_character":12},"updated":"2016-05-26 20:23:55.000000000","message":"We shouldn\u0027t need this. The default of 5 should be set in the config. Then we can access `CONF[\u0027service:worker\u0027].threads` without worrying about exceptions.","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"d06a1b20c3ad65bc8149649131b1bbdae9171981","unresolved":false,"context_lines":[{"line_number":27,"context_line":""},{"line_number":28,"context_line":""},{"line_number":29,"context_line":"def default_executor():"},{"line_number":30,"context_line":"    thread_count \u003d 5"},{"line_number":31,"context_line":"    try:"},{"line_number":32,"context_line":"        thread_count \u003d CONF[\u0027service:worker\u0027].threads"},{"line_number":33,"context_line":"    except Exception:"},{"line_number":34,"context_line":"        pass"},{"line_number":35,"context_line":""},{"line_number":36,"context_line":"    return futures.ThreadPoolExecutor(thread_count)"},{"line_number":37,"context_line":""}],"source_content_type":"text/x-python","patch_set":13,"id":"bab6814e_1e70753f","line":34,"range":{"start_line":30,"start_character":0,"end_line":34,"end_character":12},"in_reply_to":"dab17558_f60295b3","updated":"2016-05-26 21:44:45.000000000","message":"By having the default it allows us to test without doing things like mocking the the global CONF.","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"dbe120db8d2de0ef0f45ebeaf91c4160d5c350ea","unresolved":false,"context_lines":[{"line_number":45,"context_line":"    def do(task):"},{"line_number":46,"context_line":"        return task()"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def task_name(self, task):"},{"line_number":49,"context_line":"        if hasattr(task, \u0027task_name\u0027):"},{"line_number":50,"context_line":"            return str(task.task_name)"},{"line_number":51,"context_line":"        if hasattr(task, \u0027func_name\u0027):"},{"line_number":52,"context_line":"            return str(task.func_name)"},{"line_number":53,"context_line":"        return \u0027UnnamedTask\u0027"},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    def run(self, tasks):"},{"line_number":56,"context_line":"        \"\"\"Run task or set of tasks"}],"source_content_type":"text/x-python","patch_set":13,"id":"dab17558_565c41ed","line":53,"range":{"start_line":48,"start_character":0,"end_line":53,"end_character":28},"updated":"2016-05-26 20:23:55.000000000","message":"Could this be moved a property on the task, like task.name?","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"d06a1b20c3ad65bc8149649131b1bbdae9171981","unresolved":false,"context_lines":[{"line_number":45,"context_line":"    def do(task):"},{"line_number":46,"context_line":"        return task()"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"    def task_name(self, task):"},{"line_number":49,"context_line":"        if hasattr(task, \u0027task_name\u0027):"},{"line_number":50,"context_line":"            return str(task.task_name)"},{"line_number":51,"context_line":"        if hasattr(task, \u0027func_name\u0027):"},{"line_number":52,"context_line":"            return str(task.func_name)"},{"line_number":53,"context_line":"        return \u0027UnnamedTask\u0027"},{"line_number":54,"context_line":""},{"line_number":55,"context_line":"    def run(self, tasks):"},{"line_number":56,"context_line":"        \"\"\"Run task or set of tasks"}],"source_content_type":"text/x-python","patch_set":13,"id":"bab6814e_5ebb1dc4","line":53,"range":{"start_line":48,"start_character":0,"end_line":53,"end_character":28},"in_reply_to":"dab17558_565c41ed","updated":"2016-05-26 21:44:45.000000000","message":"That implies tasks are always going to be objects instead of callables. This is a pretty minimal bit of cruft to support both, but I do agree, it is a little ugly.","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"},{"author":{"_account_id":8099,"name":"Graham Hayes","email":"gr@ham.ie","username":"graham"},"change_message_id":"4ef8091d841ca9496179da69a6ce5b0536246830","unresolved":false,"context_lines":[{"line_number":71,"context_line":"        self.task_time \u003d self.end_time - self.start_time"},{"line_number":72,"context_line":""},{"line_number":73,"context_line":"        task_names \u003d [self.task_name(t) for t in tasks]"},{"line_number":74,"context_line":"        LOG.info(_LI(\"Finished Tasks %(tasks)s in %(time)fs\"),"},{"line_number":75,"context_line":"                {\u0027tasks\u0027: task_names, \u0027time\u0027: self.task_time})"},{"line_number":76,"context_line":""},{"line_number":77,"context_line":"        return results"}],"source_content_type":"text/x-python","patch_set":17,"id":"7aa08908_edeb33a2","line":74,"range":{"start_line":74,"start_character":17,"end_line":74,"end_character":20},"updated":"2016-06-14 13:05:43.000000000","message":"this should be debug - each task should log success / fail, with details.","commit_id":"99c52578479b4964b10a84cbc2e6763d3d38fd07"}],"designate/worker/rpcapi.py":[{"author":{"_account_id":8099,"name":"Graham Hayes","email":"gr@ham.ie","username":"graham"},"change_message_id":"4ef8091d841ca9496179da69a6ce5b0536246830","unresolved":false,"context_lines":[{"line_number":1,"context_line":"# Copyright 2016 Rackspace Inc."},{"line_number":2,"context_line":"#"},{"line_number":3,"context_line":"# Author: Tim Simmons \u003ctim.simmons@rackspace.com\u003e"},{"line_number":4,"context_line":"#"}],"source_content_type":"text/x-python","patch_set":17,"id":"7aa08908_cd56375b","line":1,"updated":"2016-06-14 13:05:43.000000000","message":"have a look at the central RPCAPI for how we should do RPC logging","commit_id":"99c52578479b4964b10a84cbc2e6763d3d38fd07"}],"designate/worker/service.py":[{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"c3d64d503a1a19d2223e4d27c065935792cde687","unresolved":false,"context_lines":[{"line_number":43,"context_line":""},{"line_number":44,"context_line":"        # Build the Pool (and related) Object from Config"},{"line_number":45,"context_line":"        # TODO(timsim) obviously fix this when there\u0027s \u003e1 pool"},{"line_number":46,"context_line":"        self.pool \u003d objects.Pool.from_config("},{"line_number":47,"context_line":"            CONF, CONF[\u0027service:pool_manager\u0027].pool_id)"},{"line_number":48,"context_line":""},{"line_number":49,"context_line":"        # Create the necessary Backend instances for each target"}],"source_content_type":"text/x-python","patch_set":6,"id":"1a122d0e_5a132801","line":46,"updated":"2016-05-05 16:22:17.000000000","message":"Need to replace this with the new pools-yaml/db logic to get pool info https://github.com/openstack/designate/blob/master/designate/pool_manager/service.py#L157 otherwise this won\u0027t start.","commit_id":"eae9c84e92701728b840fe81adc6ea0d8fd03e45"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"af0855ab8f4146b90ffbcd31667c9b1d9eb24916","unresolved":false,"context_lines":[{"line_number":54,"context_line":"        if not hasattr(self, \u0027_pool\u0027):"},{"line_number":55,"context_line":"            # Build the Pool (and related) Object from Config"},{"line_number":56,"context_line":"            # TODO(timsim) obviously fix this when there\u0027s \u003e1 pool"},{"line_number":57,"context_line":"            self._pool \u003d objects.Pool.from_config("},{"line_number":58,"context_line":"                CONF, CONF[\u0027service:pool_manager\u0027].pool_id"},{"line_number":59,"context_line":"            )"},{"line_number":60,"context_line":"            # Create the necessary Backend instances for each target"}],"source_content_type":"text/x-python","patch_set":8,"id":"faac7902_d2d504ba","line":57,"updated":"2016-05-05 19:35:35.000000000","message":"Still need to do this from the database rather than the config file.\nhttps://github.com/openstack/designate/blob/master/designate/pool_manager/service.py#L153-L179\n\nFor reference.","commit_id":"eba3ec7d41e2eb141cac2e509b61668045ce5f9a"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"69d2d5d87417242785fd04b96ab6631a8f9edd87","unresolved":false,"context_lines":[{"line_number":125,"context_line":"        task \u003d zonetasks.ZoneAction("},{"line_number":126,"context_line":"            self.executor, context, pool, zone, zone.action"},{"line_number":127,"context_line":"        )"},{"line_number":128,"context_line":"        return self.executor.run(task)"},{"line_number":129,"context_line":""},{"line_number":130,"context_line":"    def create_zone(self, context, zone):"},{"line_number":131,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":13,"id":"dab17558_798ba0c3","line":128,"updated":"2016-05-11 14:02:04.000000000","message":"In the executor, you removed a bit of code that will return a single result if there is only task, so these will be returning a list of values. If that is OK, then cool! Otherwise, we need to either add back the small optimization in the executor for single tasks or extract the result before returning here. \n\nI\u0027m a fan of adding back the optimization b/c it seems like it is easy enough to understand and avoids things like `executor.run(mytask)[0]` or `executor.run(mytask).pop()` both can be tricky if the result is an empty list.\n\nThat said, consistency is also a good thing, in which case there are other options like creating an `executor.run_task` that returns the result directly. Or just do run(x).pop() and see what happens!\n\nJust food for thought as this shakes out.","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"867e9c6646261e9fba82f3a5148778bf93b9889b","unresolved":false,"context_lines":[{"line_number":125,"context_line":"        task \u003d zonetasks.ZoneAction("},{"line_number":126,"context_line":"            self.executor, context, pool, zone, zone.action"},{"line_number":127,"context_line":"        )"},{"line_number":128,"context_line":"        return self.executor.run(task)"},{"line_number":129,"context_line":""},{"line_number":130,"context_line":"    def create_zone(self, context, zone):"},{"line_number":131,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":13,"id":"dab17558_da4607bf","line":128,"in_reply_to":"dab17558_798ba0c3","updated":"2016-05-16 20:10:08.000000000","message":"Yeah, in the functionaltests it became a problem because on a zone operation with one target, it was returning just one value, and then all that code for doing thresholds and percentages was jacked.\n\nYou could certainly make exceptions, or map that result to a [] for certain things, but I think it\u0027s better to just be completely consistent and be a [].","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"}],"designate/worker/tasks/base.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":32,"context_line":"class Task(object):"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"    def __init__(self, **kwargs):"},{"line_number":35,"context_line":"        self.__name__ \u003d self.__class__.__name__  # hack"},{"line_number":36,"context_line":"        self.options \u003d {}"},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"        # Do something better, config mixin?"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_cee8cb19","line":35,"updated":"2016-02-25 20:33:10.000000000","message":"if the task has a name, I think just call it `self.task_name` or something","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"3a6e537eb05b146d4fc208aeba29eaee9a720d7a","unresolved":false,"context_lines":[{"line_number":32,"context_line":"class Task(object):"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":"    def __init__(self, **kwargs):"},{"line_number":35,"context_line":"        self.__name__ \u003d self.__class__.__name__  # hack"},{"line_number":36,"context_line":"        self.options \u003d {}"},{"line_number":37,"context_line":""},{"line_number":38,"context_line":"        # Do something better, config mixin?"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_45b00cde","line":35,"in_reply_to":"9aed3d3a_cee8cb19","updated":"2016-02-26 15:34:36.000000000","message":"Agreed, I had a momentary absence of sense here.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":44,"context_line":"        # Compute a time (seconds) by which things should have propagated"},{"line_number":45,"context_line":"        self.max_prop_time \u003d (self.timeout * self.max_retries +"},{"line_number":46,"context_line":"                             self.max_retries * self.retry_interval +"},{"line_number":47,"context_line":"                             self.delay)"},{"line_number":48,"context_line":""},{"line_number":49,"context_line":"        # Get a storage connection"},{"line_number":50,"context_line":"        storage_driver \u003d cfg.CONF[\u0027service:central\u0027].storage_driver"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_0eabd38a","line":47,"updated":"2016-02-25 20:33:10.000000000","message":"If the max_prop_time is duplicated from somewhere (isn\u0027t it also in the poo manager?), then it should be refactored to a function to avoid copy-paste.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"3a6e537eb05b146d4fc208aeba29eaee9a720d7a","unresolved":false,"context_lines":[{"line_number":44,"context_line":"        # Compute a time (seconds) by which things should have propagated"},{"line_number":45,"context_line":"        self.max_prop_time \u003d (self.timeout * self.max_retries +"},{"line_number":46,"context_line":"                             self.max_retries * self.retry_interval +"},{"line_number":47,"context_line":"                             self.delay)"},{"line_number":48,"context_line":""},{"line_number":49,"context_line":"        # Get a storage connection"},{"line_number":50,"context_line":"        storage_driver \u003d cfg.CONF[\u0027service:central\u0027].storage_driver"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_25b508cd","line":47,"in_reply_to":"9aed3d3a_0eabd38a","updated":"2016-02-26 15:34:36.000000000","message":"You\u0027re right.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":59,"context_line":"        return worker_rpcapi.WorkerAPI.get_instance()"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    def __call__(self):"},{"line_number":62,"context_line":"        # You shouldn\u0027t override this"},{"line_number":63,"context_line":"        self.pre()"},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"        threadLimiter.acquire()"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_ee02ef6e","line":62,"updated":"2016-02-25 20:33:10.000000000","message":"important comments should be docstrings","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"3a6e537eb05b146d4fc208aeba29eaee9a720d7a","unresolved":false,"context_lines":[{"line_number":59,"context_line":"        return worker_rpcapi.WorkerAPI.get_instance()"},{"line_number":60,"context_line":""},{"line_number":61,"context_line":"    def __call__(self):"},{"line_number":62,"context_line":"        # You shouldn\u0027t override this"},{"line_number":63,"context_line":"        self.pre()"},{"line_number":64,"context_line":""},{"line_number":65,"context_line":"        threadLimiter.acquire()"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_85ca144e","line":62,"in_reply_to":"9aed3d3a_ee02ef6e","updated":"2016-02-26 15:34:36.000000000","message":"agreed.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":71,"context_line":"        self.post(return_value)"},{"line_number":72,"context_line":"        return return_value"},{"line_number":73,"context_line":""},{"line_number":74,"context_line":"    def pre(self, **kwargs):"},{"line_number":75,"context_line":"        self.start_time \u003d time.time()"},{"line_number":76,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.__name__)"},{"line_number":77,"context_line":"        LOG.debug(\"Current Threads #: %s\","}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_1e83fccd","line":74,"updated":"2016-02-25 20:33:10.000000000","message":"I am against accepting arbitrary parameters to these functions (pre/run/post). Since tasks are classes, these args can be passed in to the __init__ and kept as attributes. Then if you\u0027re ever passing task objects around, you never have figure out what arguments to supply to these functions.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"3a6e537eb05b146d4fc208aeba29eaee9a720d7a","unresolved":false,"context_lines":[{"line_number":71,"context_line":"        self.post(return_value)"},{"line_number":72,"context_line":"        return return_value"},{"line_number":73,"context_line":""},{"line_number":74,"context_line":"    def pre(self, **kwargs):"},{"line_number":75,"context_line":"        self.start_time \u003d time.time()"},{"line_number":76,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.__name__)"},{"line_number":77,"context_line":"        LOG.debug(\"Current Threads #: %s\","}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_e5e160d2","line":74,"in_reply_to":"9aed3d3a_1e83fccd","updated":"2016-02-26 15:34:36.000000000","message":"So am I.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":87,"context_line":"        LOG.info(_LI(\"Finished Task: %(name)s in %(time)fs\"),"},{"line_number":88,"context_line":"                {\u0027name\u0027: self.__name__, \u0027time\u0027: self.task_time})"},{"line_number":89,"context_line":""},{"line_number":90,"context_line":"    def run(self):"},{"line_number":91,"context_line":"        pass"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_01a44155","line":90,"updated":"2016-02-25 20:33:10.000000000","message":"Since this doesn\u0027t run the task (__call__ actually executes it), can we name this something other than run, like \"during\"?","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"3a6e537eb05b146d4fc208aeba29eaee9a720d7a","unresolved":false,"context_lines":[{"line_number":87,"context_line":"        LOG.info(_LI(\"Finished Task: %(name)s in %(time)fs\"),"},{"line_number":88,"context_line":"                {\u0027name\u0027: self.__name__, \u0027time\u0027: self.task_time})"},{"line_number":89,"context_line":""},{"line_number":90,"context_line":"    def run(self):"},{"line_number":91,"context_line":"        pass"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_25ece8c6","line":90,"in_reply_to":"9aed3d3a_01a44155","updated":"2016-02-26 15:34:36.000000000","message":"Oh god yes.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":39,"context_line":"        self.threshold \u003d CONF[\u0027service:worker\u0027].threshold_percentage"},{"line_number":40,"context_line":"        self.timeout \u003d CONF[\u0027service:worker\u0027].poll_timeout"},{"line_number":41,"context_line":"        self.retry_interval \u003d CONF[\u0027service:worker\u0027].poll_retry_interval"},{"line_number":42,"context_line":"        self.max_retries \u003d CONF[\u0027service:worker\u0027].poll_max_retries"},{"line_number":43,"context_line":"        self.delay \u003d CONF[\u0027service:worker\u0027].poll_delay"},{"line_number":44,"context_line":"        # Compute a time (seconds) by which things should have propagated"},{"line_number":45,"context_line":"        self.max_prop_time \u003d utils.max_prop_time(self.timeout,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_b33f9924","line":42,"updated":"2016-03-01 16:30:35.000000000","message":"Thinking aloud, it sounds like creating a RetryableMixin might be worthwhile when tests come along.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"b7e001e669080ceb0c7060ce3fb84c54dd238071","unresolved":false,"context_lines":[{"line_number":39,"context_line":"        self.threshold \u003d CONF[\u0027service:worker\u0027].threshold_percentage"},{"line_number":40,"context_line":"        self.timeout \u003d CONF[\u0027service:worker\u0027].poll_timeout"},{"line_number":41,"context_line":"        self.retry_interval \u003d CONF[\u0027service:worker\u0027].poll_retry_interval"},{"line_number":42,"context_line":"        self.max_retries \u003d CONF[\u0027service:worker\u0027].poll_max_retries"},{"line_number":43,"context_line":"        self.delay \u003d CONF[\u0027service:worker\u0027].poll_delay"},{"line_number":44,"context_line":"        # Compute a time (seconds) by which things should have propagated"},{"line_number":45,"context_line":"        self.max_prop_time \u003d utils.max_prop_time(self.timeout,"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_6799c76e","line":42,"in_reply_to":"7af24918_b33f9924","updated":"2016-03-01 17:04:36.000000000","message":"Yeah, really this stuff could go in a subset of the actual tasks. This is very WIP.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":43,"context_line":"        self.delay \u003d CONF[\u0027service:worker\u0027].poll_delay"},{"line_number":44,"context_line":"        # Compute a time (seconds) by which things should have propagated"},{"line_number":45,"context_line":"        self.max_prop_time \u003d utils.max_prop_time(self.timeout,"},{"line_number":46,"context_line":"            self.max_retries, self.retry_interval, self.delay)"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"        # Get a storage connection"},{"line_number":49,"context_line":"        storage_driver \u003d cfg.CONF[\u0027service:central\u0027].storage_driver"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_9351bd65","line":46,"updated":"2016-03-01 16:30:35.000000000","message":"I suspect that is a convenience that wouldn\u0027t go in a base task.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"b7e001e669080ceb0c7060ce3fb84c54dd238071","unresolved":false,"context_lines":[{"line_number":43,"context_line":"        self.delay \u003d CONF[\u0027service:worker\u0027].poll_delay"},{"line_number":44,"context_line":"        # Compute a time (seconds) by which things should have propagated"},{"line_number":45,"context_line":"        self.max_prop_time \u003d utils.max_prop_time(self.timeout,"},{"line_number":46,"context_line":"            self.max_retries, self.retry_interval, self.delay)"},{"line_number":47,"context_line":""},{"line_number":48,"context_line":"        # Get a storage connection"},{"line_number":49,"context_line":"        storage_driver \u003d cfg.CONF[\u0027service:central\u0027].storage_driver"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_c717fbeb","line":46,"in_reply_to":"7af24918_9351bd65","updated":"2016-03-01 17:04:36.000000000","message":"Yeah this should go only in the tasks that need it.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_5681e349","line":66,"updated":"2016-03-01 16:30:35.000000000","message":"If this shouldn\u0027t be overridden it seems like you want to extract the API (ie pre / actify / post) from executing the tasks. That way tasks can use things like service access via mixins as needed and the executor only deals with executing tasks. Or, the executor makes available services to tasks along with config. I prefer the former, but hopefully that makes some sense ;)","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"46c0f84a801d2f969c2892174b607dae8632cc20","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_ecaad06f","line":66,"in_reply_to":"7af24918_09772e3b","updated":"2016-03-01 20:10:54.000000000","message":"Things can get a little hairy with subtasks calling subtasks b/c you need to pass around concerns. If task A needs x, y and task B need z, but A calls B, that means A needs x, y an z, the end result being testing A means mocking z only b/c B needs it. Keeping that state in an executor or engine makes that simpler and allows to you say no nested tasks. At least that is where I can imagine tests getting sketchy.\n\nIn any case, I totally agree. Working code beats imaginary code!","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"5e64dab15a0445d565710c51c51860517a936663","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_7d103a4f","line":66,"in_reply_to":"7af24918_472beb30","updated":"2016-03-01 17:22:03.000000000","message":"I suppose what I\u0027d expect to see is something along the lines of:\n\n    executor \u003d Executor()\n    executor.add_task(Foo(bar\u003d\u0027baz\u0027))\n    executor.execute()\n\nAt which point, the executor can pretty much do a for t in tasks t.pre(), t.do(), t.post(). That said, if the executor takes a callable, you can do pre / post in the actual task as needed, again using something like mixins (or just helper functions) to DRY.\n\n    class Task(NotifyMixin):\n        def __call__(self):\n            self.notifier.send(\u0027hello there!!!\u0027)\n\nNow, the reason for the mixin bits as I suggested, was b/c as soon as you say everything gets all the things, even if it doesn\u0027t need them, it can be tricky then to isolate problems. What happens if your db connection, that you don\u0027t need, can\u0027t connect? Having a way for your tasks pull in its dependencies is one way to do this and mixins is one way to do it. In other words, you compose your Task from dependency mixins that provide functionality / connections / etc. Basically, instead of each task having:\n\n    @property\n    def storage(self):\n        if hasattr(self, \u0027_storage\u0027):\n            self._storage \u003d get_storage()\n        return self._storage\n\nYou can just include the mixin to make it available.\n\n    class MyTask(StorageMixin) \n\nNot saying it is the best way to do it, but simply a way to do it. \n\nHope that clarifies a little.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"b7e001e669080ceb0c7060ce3fb84c54dd238071","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_472beb30","line":66,"in_reply_to":"7af24918_5681e349","updated":"2016-03-01 17:04:36.000000000","message":"So I\u0027m not really clear here.\n\nThe pre/actify/post have nothing to do with the actual execution of the task, they\u0027re just the things that get done during the task.\n\nWell really, actify is the only thing that actually gets done. pre/post are just conveniences that allow you to do something like blast a notification or whatever.\n\nPglbutt mentioned having a task executor that held this type of behavior, and I understand the idea. But I can\u0027t really think of any additional value you get from doing that. I\u0027m not the biggest fan of executing a task by calling Task()(), but I think having another object for basically just this logic here doesn\u0027t seem any better. I guess it might look a little nicer looking to:\n\n  def create_zone():\n      self.executor.execute(tasks.CreateZone(name\u003dblah.com))\n\nBut I\u0027m loathe to creating that additional complexity just for that reason. I don\u0027t think this interface excludes using \"things like service access via mixins as needed\". Although I despise Mixins for that type of thing. Having some parent executor that holds keys that a task needs doesn\u0027t seem ideal to me either because I enjoy the idea that this task (although occasionally containing things it may not need), is completely self contained. It is a true unit of work, and when it dies, the work has concluded.\n\nThis was the main thing I wanted feedback on this patch. Seeing the actual tasks I created that do work (well) should give a better idea of what this actually looks like in practice.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"7d7f798ee1f90131d783e2d5df024294835bbec1","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_76bfa6fb","line":66,"in_reply_to":"7af24918_70dcce12","updated":"2016-03-01 22:57:19.000000000","message":"Yeah, but you\u0027ve created a dependency to patch the imported object as well as ensuring the parent has all the information necessary to instantiate the subtask. That is not horrible, but if you can make the subtask more injectable, that is better. It also will be simpler over time if you don\u0027t have to pass into parent tasks the requirements of the subtask. That is a simple thing off hand, but from a design perspective, it has the potential to get long in the tooth. \n\nFor example, If you have A -\u003e B -\u003e C and say C starts to call D and D has 3 new arguments. You have to cascade that change through each parent task and that relationship isn\u0027t explicitly defined. In other words, you might also have a X -\u003e Y -\u003e C chain somewhere else that may have had incomplete tests and you break things. This is why an executor or engine can be nice b/c you can define the relationship between everything while asserting each component really can act on its own, the result being dependencies across tasks can be managed. \n\nThe downside is that you do have a tool like taskin or TaskFlow that provides some limits to your task code in the same way a MapReduce framework might be cumbersome. Personally, I\u0027m OK with the limits as most can be worked around in a way that isn\u0027t too painful, especially in a limited environment. In other words, if we merged in taskin, we\u0027d customize it as needed without a concern for how others use it. So, adding something like a \"wait for zone status\" at the engine level, would be something we could do, where in other frameworks, that wouldn\u0027t be possible. \n\nI\u0027m slowly convincing myself taskin could be nice here ;)","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"e21dd6bb2666666bdd43b0dc7822affb997a0a31","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_f8d45848","line":66,"in_reply_to":"7af24918_7d103a4f","updated":"2016-03-01 17:35:01.000000000","message":"So I just don\u0027t really see the added benefit you get from having an `executor` in that case. I\u0027m sure something would pop up, but essentially you\u0027re just transferring the logic from the Base Task to an executor. At that point it seems like a matter of semantics.\n\nThe other thing that gets wonky with an `executor` is nested Tasks. CreateZone -\u003e CreateZoneOnTarget -\u003e Poll. Unless you start defining those Tasks in such a way that CreateZone returns...more tasks or some state of things that you then feed into more tasks. But that gets really complex really quick. The kind of \u0027dirtiness\u0027 that this presents at first sight, in my opinion, is worth the simplicity. Except for the horrible \u0027actify\u0027 name, these Tasks are exceedingly simple to understand to a layman, and crazy simple to swap out/customize.\n\n\nFor stuff like a storage connection, or things that are external like that, it seems like a perfectly valid pattern for it to be a lazily loaded property so that if it\u0027s never exercised it\u0027s a noop. I\u0027d argue that isolating problems gets trickier when you have:\n\n  class MyTask(StorageMixin, NotifierMixin, RetryMixin, BackendMixin, PoolMixin):\n\nWhenever you start dealing with \u003e1 parents I\u0027m totally \"boo inheritance\" for the very reason that you\u0027ve then got to track down logic that a thing is using that\u0027s not in the thing itself, but that\u0027s just my opinion.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"5121845eb05a3dd589eec556d9128e0f91ee39b8","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_09772e3b","line":66,"in_reply_to":"7af24918_bcfe3d69","updated":"2016-03-01 19:57:32.000000000","message":"So pglass gave me a compelling reason for an executor, like a base task, you\u0027ve got one way to run all tasks. But if for some reason you wanted to change that method, it\u0027s as simple as getting a different \"executor\". I can see a \"testexecutor\", a \"debugexecutor\", a \"hphelionexecutor\". This gets weird when you have tasks that nest though, because you\u0027ve either got to pass the executor around, or go and get a new one from a pluggable place every time you want to exec a \"sub\" task.\n\nBut as for tests, I don\u0027t really think it gets much simpler than a 10 line `actify` function that you can Mock things in and make sure it does what you want. You can instantiate the Task without executing it. That\u0027s like the best thing ever for a Unit test right? Being able to inspect the instance vars and call the functions with scoped Mocks?\n\nThe API for _executing_ the tasks as they are is still making them callable. The API wherein (pre/actify/post) isn\u0027t exposed outside of the Task itself. It\u0027s contained, and simple, and testable.\n\nPre and Post can go down the toilet if it\u0027s not desirable. To some degree you could put that in the executor, but there\u0027s still going to come a day when you want to add that type of thing to just one task, and people will not be happy that it\u0027s in the main do, actify, __call__, whatever function.\n\nThe reason I like this approach over something Taskin-akin is that there is almost no complexity here. The barrier-to-entry for writing new tasks, or extending existing ones is super low. While you don\u0027t get to generalize certain things, it\u0027s just dead simple. But the tradeoff is that when tasks nest, that\u0027s not as visible. You don\u0027t really have the definition of the Flow, without reading the (short) Tasks to see what other pieces of work they invoke, or read the log. \n\nThis is kind of sad, but on the flip side, you avoid the fiery dragons of trying to generalize all the \u0027flows\u0027 in a framework, and get to focus on doing the actual work. With nice logs, it gets pretty easy to see what a task is doing.\n\nI mean same, it\u0027s all IMHO. I don\u0027t really want it to sound like I\u0027m defending all the great work I\u0027ve done here, because it\u0027s really not all that much, or all that great. But I know it seems to work really well thus far. So that kind of sways me to this way of thinking.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"4430e68ac2ad43af94f9cd630cbd8c9d2038aff9","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_70dcce12","line":66,"in_reply_to":"7af24918_ecaad06f","updated":"2016-03-01 22:23:45.000000000","message":"But that\u0027s not how you\u0027d test it though. If you were writing Unit Tests for a task \"CreateZone\" which ends up calling subtask \"CreateZoneOnTarget\" which ends up calling Task \"Foo\". You\u0027d only have to mock \"CreateZoneOnTarget\" right?","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"4424e1ef5b1e5c43dc48f1666bc49709ba891d67","unresolved":false,"context_lines":[{"line_number":63,"context_line":"        the task executes. All the work done in `do()` along with the pre/post"},{"line_number":64,"context_line":"        functions are called."},{"line_number":65,"context_line":""},{"line_number":66,"context_line":"        This should NOT be overridden."},{"line_number":67,"context_line":"        \"\"\""},{"line_number":68,"context_line":"        self.start_time \u003d time.time()"},{"line_number":69,"context_line":"        LOG.info(_LI(\"Starting Task: %s\"), self.task_name)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_bcfe3d69","line":66,"in_reply_to":"7af24918_f8d45848","updated":"2016-03-01 19:34:35.000000000","message":"I suppose it gets more complicated when I think of the tests. The executor can be responsible for testing and asserting the progression of tasks works as expected. When the executor has to think about pre, do and post, that multiplies everything by 3. Then there is the fact that your API is dependent on inheriting a base class, which means there are questions as to what happens when that API needs to change when other nodes might be on differing version (ie the worker model means there might be a lot more workers, so I\u0027m thinking of a rolling restart across 100s of workers). In my mind you can split up the concerns and complexity by having an executor (or something that manages the set of tasks) separate from tasks. Then, by making the API to calling a task a callable, you keep a really simple API over time. \n\nWith regard to nested tasks, I think taskin sort of does that where the engine essentially handles the flattening of the tasks in order to avoid things like recursive locks and the like that gets sketchy. \n\nFinally, I should make it clear a Mixin is NOT a class inheritance. The idea with mixins is that you have classes you \"mix in\" to a normal class. I totally and completely agree with the barftasticness of multiple parent inheritance. Therefore they should have no outside dependencies and simply allow adding orthogonal functionality. This is also why I am fan of a callable vs. a base Task class. You hand off the implementation of the task without much framework to keep track of. \n\nSo, this is all IMHO. I\u0027m curious what others think. Also, assuming this gets split up into other patches, we can submit new reviews that experiment with different ideas. \n\n!m","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":97,"context_line":"    def post(self, return_value):"},{"line_number":98,"context_line":"        pass"},{"line_number":99,"context_line":""},{"line_number":100,"context_line":"    def actify(self):"},{"line_number":101,"context_line":"        \"\"\""},{"line_number":102,"context_line":"        Override this to do the needful for a task"},{"line_number":103,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_562ba330","line":100,"updated":"2016-03-01 16:30:35.000000000","message":"I\u0027m just going to give it to you straight. This is a horrible name ;)\n\nThat said, if you extract the executor from the task, you could use __call__. At the very least, I\u0027d say `do` is better than `actify`. Even though `actify` does seem like a cool project name!","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"b7e001e669080ceb0c7060ce3fb84c54dd238071","unresolved":false,"context_lines":[{"line_number":97,"context_line":"    def post(self, return_value):"},{"line_number":98,"context_line":"        pass"},{"line_number":99,"context_line":""},{"line_number":100,"context_line":"    def actify(self):"},{"line_number":101,"context_line":"        \"\"\""},{"line_number":102,"context_line":"        Override this to do the needful for a task"},{"line_number":103,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_6c0feee1","line":100,"in_reply_to":"7af24918_562ba330","updated":"2016-03-01 17:04:36.000000000","message":"Blame pglbutt. I hated it to but couldn\u0027t come up with anything better, because this isn\u0027t actually executing the task, it\u0027s just defining all the work that will take place when the task is executed.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e4f2694cc4b9f3d2052ea55fd1f02b5a499fb282","unresolved":false,"context_lines":[{"line_number":34,"context_line":""},{"line_number":35,"context_line":"    @property"},{"line_number":36,"context_line":"    def config(self):"},{"line_number":37,"context_line":"        if not hasattr(self, \u0027_config\u0027):"},{"line_number":38,"context_line":"            self._config \u003d CONF[\u0027service:worker\u0027]"},{"line_number":39,"context_line":"        return self._config"},{"line_number":40,"context_line":""},{"line_number":41,"context_line":"    @property"}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_35ff1a35","line":38,"range":{"start_line":37,"start_character":8,"end_line":38,"end_character":49},"updated":"2016-04-29 16:40:59.000000000","message":"It would be nice if we could refactor this pattern, since it is used frequently:\n\n    @lazy_property\n    def config(self):\n        return CONF[\u0027service:worker\u0027]\n\nOr something like that? Idk.","commit_id":"924963512198eb967f65208060908af146a293b0"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e4f2694cc4b9f3d2052ea55fd1f02b5a499fb282","unresolved":false,"context_lines":[{"line_number":147,"context_line":"    def post(self, return_value):"},{"line_number":148,"context_line":"        pass"},{"line_number":149,"context_line":""},{"line_number":150,"context_line":"    def actify(self):"},{"line_number":151,"context_line":"        \"\"\""},{"line_number":152,"context_line":"        Override this to do the needful for a task"},{"line_number":153,"context_line":"        \"\"\""}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_50fbc4c0","line":150,"range":{"start_line":150,"start_character":8,"end_line":150,"end_character":14},"updated":"2016-04-29 16:40:59.000000000","message":"\"invoke\"\n\"execute\"\n\"main\"\n\nAlternatively, if we remove pre/post, we can remove this function and just use __call__.","commit_id":"924963512198eb967f65208060908af146a293b0"}],"designate/worker/tasks/zone.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":179,"context_line":"        return True"},{"line_number":180,"context_line":""},{"line_number":181,"context_line":""},{"line_number":182,"context_line":"class ZoneAction(base.Task):"},{"line_number":183,"context_line":""},{"line_number":184,"context_line":"    def __init__(self, context, pool, zone):"},{"line_number":185,"context_line":"        super(ZoneAction, self).__init__()"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_41dac979","line":182,"updated":"2016-02-25 20:33:10.000000000","message":"It would be nice to have a one line docstring describing each task.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"3a6e537eb05b146d4fc208aeba29eaee9a720d7a","unresolved":false,"context_lines":[{"line_number":179,"context_line":"        return True"},{"line_number":180,"context_line":""},{"line_number":181,"context_line":""},{"line_number":182,"context_line":"class ZoneAction(base.Task):"},{"line_number":183,"context_line":""},{"line_number":184,"context_line":"    def __init__(self, context, pool, zone):"},{"line_number":185,"context_line":"        super(ZoneAction, self).__init__()"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_05b604dc","line":182,"in_reply_to":"9aed3d3a_41dac979","updated":"2016-02-26 15:34:36.000000000","message":"It sure would","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":223,"context_line":"                   \u0027target\u0027: self.target})"},{"line_number":224,"context_line":""},{"line_number":225,"context_line":"        retries \u003d 0"},{"line_number":226,"context_line":"        while retries \u003c self.max_retries:"},{"line_number":227,"context_line":"            try:"},{"line_number":228,"context_line":"                if self.action \u003d\u003d \u0027CREATE\u0027:"},{"line_number":229,"context_line":"                    self.target.backend.create_zone(self.context, self.zone)"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_61f8ad7d","line":226,"updated":"2016-02-25 20:33:10.000000000","message":"`for retries in range(0, self.max_retries)` ?","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"3a6e537eb05b146d4fc208aeba29eaee9a720d7a","unresolved":false,"context_lines":[{"line_number":223,"context_line":"                   \u0027target\u0027: self.target})"},{"line_number":224,"context_line":""},{"line_number":225,"context_line":"        retries \u003d 0"},{"line_number":226,"context_line":"        while retries \u003c self.max_retries:"},{"line_number":227,"context_line":"            try:"},{"line_number":228,"context_line":"                if self.action \u003d\u003d \u0027CREATE\u0027:"},{"line_number":229,"context_line":"                    self.target.backend.create_zone(self.context, self.zone)"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_c5ad3c8a","line":226,"in_reply_to":"9aed3d3a_61f8ad7d","updated":"2016-02-26 15:34:36.000000000","message":"Yup","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"8d4c72d51b6f73fe9b180f1237818c4e40afec64","unresolved":false,"context_lines":[{"line_number":167,"context_line":""},{"line_number":168,"context_line":"            UpdateStatus(context, zone)()"},{"line_number":169,"context_line":"            return False"},{"line_number":170,"context_line":"            # TODO(timsim): This is kinda gross"},{"line_number":171,"context_line":""},{"line_number":172,"context_line":"        LOG.debug(\"Found success for %(zone)s at serial %(serial)d\","},{"line_number":173,"context_line":"                  {\u0027zone\u0027: zone.name, \u0027serial\u0027: zone.serial})"}],"source_content_type":"text/x-python","patch_set":3,"id":"1a122d0e_8b5d134b","line":170,"updated":"2016-04-21 16:49:11.000000000","message":"Can you walk me through this part? We\u0027ve got enough positives on the nameservers to consider it a SUCCESS, but we still check if the no_zones are under the same threshold. \n\nWalking through this, lets say we do an update and we have 10 servers to update and a 80% threshold. If, when parsing the results, we see 8/10 serials that are greater than or equal to the zone.serial we hit our positives and get to this code. Assuming the last two were just lower serials and were 0 (2 no_zones) we still check if 20% is over our threshold of 80% and skip this. \n\nMassaging the numbers a bit, if we had a low threshold of 30%, we could get 4 positives, 4 no zones and 2 lesser serials, and then this code would be reached. I\u0027m assuming this is the use case we\u0027re trying to hit then where the threshold for positives is met and the zone does exist on enough nameservers. \n\nDoes that sound correct?","commit_id":"e8927108dc97c7ec4281729edcd3772e6ebc8677"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e4f2694cc4b9f3d2052ea55fd1f02b5a499fb282","unresolved":false,"context_lines":[{"line_number":198,"context_line":"        return True"},{"line_number":199,"context_line":""},{"line_number":200,"context_line":"    def threshold_met(self, query_result):"},{"line_number":201,"context_line":"        \"\"\"Return an boolean and error status whether the thresholds"},{"line_number":202,"context_line":"        were met."},{"line_number":203,"context_line":""},{"line_number":204,"context_line":"        The error status should be either:"}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_fb5ab180","line":201,"range":{"start_line":201,"start_character":18,"end_line":201,"end_character":28},"updated":"2016-04-29 16:40:59.000000000","message":"\"a boolean\"","commit_id":"924963512198eb967f65208060908af146a293b0"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"e4f2694cc4b9f3d2052ea55fd1f02b5a499fb282","unresolved":false,"context_lines":[{"line_number":204,"context_line":"        The error status should be either:"},{"line_number":205,"context_line":""},{"line_number":206,"context_line":"         - ERROR: the operation failed"},{"line_number":207,"context_line":"         - NO_ZONE: the zone doesnt\u0027 exist on enough name servers"},{"line_number":208,"context_line":"        \"\"\""},{"line_number":209,"context_line":""},{"line_number":210,"context_line":"        total \u003d len(query_result.results)"}],"source_content_type":"text/x-python","patch_set":4,"id":"1a122d0e_9b4c5dc9","line":207,"range":{"start_line":207,"start_character":29,"end_line":207,"end_character":36},"updated":"2016-04-29 16:40:59.000000000","message":"\"doesn\u0027t\"","commit_id":"924963512198eb967f65208060908af146a293b0"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"511b8c81c5bcd70162113724a3c313a7df84a532","unresolved":false,"context_lines":[{"line_number":254,"context_line":"        self.task_name \u003d \u0027ZoneAction-%s\u0027 % self.action.title()"},{"line_number":255,"context_line":""},{"line_number":256,"context_line":"    def wait_for_nameservers(self):"},{"line_number":257,"context_line":"        # Poll on the nameservers"},{"line_number":258,"context_line":"        time.sleep(self.delay)"},{"line_number":259,"context_line":""},{"line_number":260,"context_line":"    def zone_action_on_targets(self):"}],"source_content_type":"text/x-python","patch_set":6,"id":"1a122d0e_44d193fa","line":257,"updated":"2016-05-05 15:54:23.000000000","message":"This is just a \"polling delay\", the idea is that you send your notify or whatever, and give some small amount of time before polling happens (to do axfrs and stuff) before you start wasting resources polling.","commit_id":"eae9c84e92701728b840fe81adc6ea0d8fd03e45"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"2ef907b062800e2e8bff8dcf267ab92b9a180e85","unresolved":false,"context_lines":[{"line_number":254,"context_line":"        self.task_name \u003d \u0027ZoneAction-%s\u0027 % self.action.title()"},{"line_number":255,"context_line":""},{"line_number":256,"context_line":"    def wait_for_nameservers(self):"},{"line_number":257,"context_line":"        # Poll on the nameservers"},{"line_number":258,"context_line":"        time.sleep(self.delay)"},{"line_number":259,"context_line":""},{"line_number":260,"context_line":"    def zone_action_on_targets(self):"}],"source_content_type":"text/x-python","patch_set":6,"id":"1a122d0e_9a93004f","line":257,"in_reply_to":"1a122d0e_44d193fa","updated":"2016-05-05 16:23:58.000000000","message":"That is what I thought. Should we rename this method then?","commit_id":"eae9c84e92701728b840fe81adc6ea0d8fd03e45"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"78a455333e4781b1c387fe3d8bf427959ea583d2","unresolved":false,"context_lines":[{"line_number":254,"context_line":"        self.task_name \u003d \u0027ZoneAction-%s\u0027 % self.action.title()"},{"line_number":255,"context_line":""},{"line_number":256,"context_line":"    def wait_for_nameservers(self):"},{"line_number":257,"context_line":"        # Poll on the nameservers"},{"line_number":258,"context_line":"        time.sleep(self.delay)"},{"line_number":259,"context_line":""},{"line_number":260,"context_line":"    def zone_action_on_targets(self):"}],"source_content_type":"text/x-python","patch_set":6,"id":"1a122d0e_7c37ffe2","line":257,"in_reply_to":"1a122d0e_9a93004f","updated":"2016-05-05 17:19:54.000000000","message":"Based on the comment, I just thought it needed clarifying. You\u0027re _not_ polling during this time, just waiting for things to propagate.","commit_id":"eae9c84e92701728b840fe81adc6ea0d8fd03e45"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"dbe120db8d2de0ef0f45ebeaf91c4160d5c350ea","unresolved":false,"context_lines":[{"line_number":94,"context_line":"    \u0027DNSQueryResult\u0027, ["},{"line_number":95,"context_line":"        \u0027positives\u0027,"},{"line_number":96,"context_line":"        \u0027no_zones\u0027,"},{"line_number":97,"context_line":"        \u0027concensus_serial\u0027,"},{"line_number":98,"context_line":"        \u0027results\u0027"},{"line_number":99,"context_line":"    ]"},{"line_number":100,"context_line":")"}],"source_content_type":"text/x-python","patch_set":13,"id":"dab17558_7698656b","line":97,"range":{"start_line":97,"start_character":9,"end_line":97,"end_character":18},"updated":"2016-05-26 20:23:55.000000000","message":"This is spelled \"consensus\".","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"dbe120db8d2de0ef0f45ebeaf91c4160d5c350ea","unresolved":false,"context_lines":[{"line_number":105,"context_line":"    results is a [serial/None, ...]"},{"line_number":106,"context_line":"    \"\"\""},{"line_number":107,"context_line":"    delete \u003d zone.action \u003d\u003d \u0027DELETE\u0027"},{"line_number":108,"context_line":"    positives \u003d 0"},{"line_number":109,"context_line":"    no_zones \u003d 0"},{"line_number":110,"context_line":"    low_serial \u003d 0"},{"line_number":111,"context_line":""},{"line_number":112,"context_line":"    for serial in results:"},{"line_number":113,"context_line":"        if serial is None:"}],"source_content_type":"text/x-python","patch_set":13,"id":"dab17558_b6880dc8","line":110,"range":{"start_line":108,"start_character":0,"end_line":110,"end_character":18},"updated":"2016-05-26 20:23:55.000000000","message":"Maybe DNSQueryResult could be something (like just a class) which initializes these fields to zero, since that happens in more than one place.","commit_id":"a90d7939ada07cc2b5e14c2c5ec870f82a5359d9"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"91aef228eede71ab343564c9908eb438853fb2cb","unresolved":false,"context_lines":[{"line_number":208,"context_line":"        is_not_delete \u003d self.zone.action !\u003d \u0027DELETE\u0027"},{"line_number":209,"context_line":""},{"line_number":210,"context_line":"        # Ensure if we don\u0027t have too many nameservers that"},{"line_number":211,"context_line":"        # don\u0027t have the zone."},{"line_number":212,"context_line":"        over_no_zone_threshold \u003d self.compare_threshold("},{"line_number":213,"context_line":"            (total - query_result.no_zones), total"},{"line_number":214,"context_line":"        )"}],"source_content_type":"text/x-python","patch_set":17,"id":"9abb7d3a_c0afbe92","line":211,"updated":"2016-06-02 21:31:36.000000000","message":"here","commit_id":"99c52578479b4964b10a84cbc2e6763d3d38fd07"}],"designate/worker/utils.py":[{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":69,"context_line":""},{"line_number":70,"context_line":""},{"line_number":71,"context_line":"def send_dns_msg(dns_message, host, port\u003d53):"},{"line_number":72,"context_line":"    # This can raise some exceptions, but we\u0027ll catch them elsewhere"},{"line_number":73,"context_line":"    if not CONF[\u0027service:mdns\u0027].all_tcp:"},{"line_number":74,"context_line":"        return dns.query.udp("},{"line_number":75,"context_line":"            dns_message, host, port\u003dport, timeout\u003d10)"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_bc2018f9","line":72,"updated":"2016-02-25 20:33:10.000000000","message":"boo exceptions","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":11662,"name":"Paul Glass","email":"paul.glass@rackspace.com","username":"pglass"},"change_message_id":"07f5811dc4cf2356c3e60b33671e6ec69a3196d6","unresolved":false,"context_lines":[{"line_number":81,"context_line":"def get_serial(zone_name, host, port\u003d53):"},{"line_number":82,"context_line":"    \"\"\""},{"line_number":83,"context_line":"    Possibly raises dns.exception.Timeout or dns.query.BadResponse."},{"line_number":84,"context_line":"    Possibly returns None if, e.g., the answer section is empty."},{"line_number":85,"context_line":"    \"\"\""},{"line_number":86,"context_line":"    resp \u003d dig(zone_name, host, dns.rdatatype.SOA, port\u003dport)"},{"line_number":87,"context_line":"    if not resp.answer:"}],"source_content_type":"text/x-python","patch_set":1,"id":"9aed3d3a_3c8368ce","line":84,"updated":"2016-02-25 20:33:10.000000000","message":"This returns 0, not None.","commit_id":"9f11261a7a4d0df6e307f0b97b1ce6fdc0a299db"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":34,"context_line":"    return results"},{"line_number":35,"context_line":""},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"class ResultThread(threading.Thread):"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def __init__(self, func, *args, **kwargs):"},{"line_number":40,"context_line":"        super(ResultThread, self).__init__(*args, **kwargs)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_36889722","line":37,"updated":"2016-03-01 16:30:35.000000000","message":"This feels like it should be built into the task.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":8174,"name":"Tim Simmons","email":"tim.r.simmons@gmail.com","username":"TimSimmons"},"change_message_id":"b7e001e669080ceb0c7060ce3fb84c54dd238071","unresolved":false,"context_lines":[{"line_number":34,"context_line":"    return results"},{"line_number":35,"context_line":""},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"class ResultThread(threading.Thread):"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def __init__(self, func, *args, **kwargs):"},{"line_number":40,"context_line":"        super(ResultThread, self).__init__(*args, **kwargs)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_475f6b72","line":37,"in_reply_to":"7af24918_36889722","updated":"2016-03-01 17:04:36.000000000","message":"I don\u0027t think so. This is just an option you have when you\u0027re executing a function or Task. Sometimes I might want to pop a few threads and do things in parallel, other times I might just be executing the one Task and don\u0027t care about that.\n\nHonestly I was waiting to hear that this won\u0027t work or is a horrible idea, because I was shocked that this type of thing wasn\u0027t in the stdlib.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"},{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"5e64dab15a0445d565710c51c51860517a936663","unresolved":false,"context_lines":[{"line_number":34,"context_line":"    return results"},{"line_number":35,"context_line":""},{"line_number":36,"context_line":""},{"line_number":37,"context_line":"class ResultThread(threading.Thread):"},{"line_number":38,"context_line":""},{"line_number":39,"context_line":"    def __init__(self, func, *args, **kwargs):"},{"line_number":40,"context_line":"        super(ResultThread, self).__init__(*args, **kwargs)"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_bdbbe20d","line":37,"in_reply_to":"7af24918_475f6b72","updated":"2016-03-01 17:22:03.000000000","message":"I suppose in my mind, I\u0027m thinking that there will be an executor that will keep track of state, so tasks return something that can be piped through. It would be up to the task to use it or not. \n\nWe will definitely get a better idea as more tasks get sorted out.","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"}],"designate/zone_manager/tasks.py":[{"author":{"_account_id":15171,"name":"Eric Larson","email":"eric.larson@rackspace.com","username":"elarson"},"change_message_id":"1963419483b08e27b2a4f31202345c2b890adcb2","unresolved":false,"context_lines":[{"line_number":59,"context_line":""},{"line_number":60,"context_line":"    @property"},{"line_number":61,"context_line":"    def worker_api(self):"},{"line_number":62,"context_line":"        return worker_rpcapi.WorkerAPI.get_instance()"},{"line_number":63,"context_line":""},{"line_number":64,"context_line":"    def on_partition_change(self, my_partitions, members, event):"},{"line_number":65,"context_line":"        \"\"\"Refresh partitions attribute"}],"source_content_type":"text/x-python","patch_set":2,"id":"7af24918_76997f6e","line":62,"updated":"2016-03-01 16:30:35.000000000","message":"lazify ;)","commit_id":"57b9eba24f808b41a12a34c1f9b1e93f501416d8"}],"etc/designate/designate.conf.sample":[{"author":{"_account_id":741,"name":"Kiall Mac Innes","email":"kiall@macinnes.ie","username":"kiall"},"change_message_id":"0e3b4dbd0a95f5dc3c9abcbb35618c53cb6114b4","unresolved":false,"context_lines":[{"line_number":375,"context_line":"#-----------------------"},{"line_number":376,"context_line":"[service:worker]"},{"line_number":377,"context_line":"# Whether to send events to worker instead of Pool Manager"},{"line_number":378,"context_line":"# enabled \u003d False"},{"line_number":379,"context_line":""},{"line_number":380,"context_line":"# Number of Worker processes to spawn"},{"line_number":381,"context_line":"#workers \u003d None"}],"source_content_type":"application/octet-stream","patch_set":37,"id":"1ac06dbe_8eb93a7c","line":378,"range":{"start_line":378,"start_character":1,"end_line":378,"end_character":2},"updated":"2016-08-25 17:57:30.000000000","message":"Nit: extra space","commit_id":"81ce132e900a1b00f9acdfbfab4c9aa147095ec9"},{"author":{"_account_id":741,"name":"Kiall Mac Innes","email":"kiall@macinnes.ie","username":"kiall"},"change_message_id":"0e3b4dbd0a95f5dc3c9abcbb35618c53cb6114b4","unresolved":false,"context_lines":[{"line_number":402,"context_line":"#poll_delay \u003d 5"},{"line_number":403,"context_line":""},{"line_number":404,"context_line":"# Whether to allow worker to send NOTIFYs. NOTIFY requests to mdns will noop"},{"line_number":405,"context_line":"# notify \u003d False"},{"line_number":406,"context_line":""},{"line_number":407,"context_line":"###################################"},{"line_number":408,"context_line":"## Pool Manager Cache Configuration"}],"source_content_type":"application/octet-stream","patch_set":37,"id":"1ac06dbe_0ea6ea52","line":405,"range":{"start_line":405,"start_character":1,"end_line":405,"end_character":2},"updated":"2016-08-25 17:57:30.000000000","message":"Nit: extra space","commit_id":"81ce132e900a1b00f9acdfbfab4c9aa147095ec9"}]}
