)]}'
{"ironic_python_agent/flow.py":[{"author":{"_account_id":10343,"name":"Jim Rollenhagen","email":"jim@jimrollenhagen.com","username":"jimrollenhagen"},"change_message_id":"559708571a7951d898f02ff937adfdac55eab076","unresolved":false,"context_lines":[{"line_number":69,"context_line":"    def validate_exts(self, flow\u003dNone):"},{"line_number":70,"context_line":"        # Validates the existance of every extension required by current flow"},{"line_number":71,"context_line":"        for task in flow:"},{"line_number":72,"context_line":"            for method, _ in task.iteritems():"},{"line_number":73,"context_line":"                ext_name \u003d utils.split_command(method)[0]"},{"line_number":74,"context_line":"                if ext_name not in self.flow_mgr.names():"},{"line_number":75,"context_line":"                    raise errors.InvalidCommandParamsError("}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAWH%2F9P4s%3D","line":72,"updated":"2014-04-07 17:45:07.000000000","message":"Could just do `for method in task` here, no?","commit_id":"a2e06066fbe68936e2735f969f0cd92b7adf5fee"},{"author":{"_account_id":10343,"name":"Jim Rollenhagen","email":"jim@jimrollenhagen.com","username":"jimrollenhagen"},"change_message_id":"559708571a7951d898f02ff937adfdac55eab076","unresolved":false,"context_lines":[{"line_number":79,"context_line":"    def start_flow(self, command_name, flow\u003dNone):"},{"line_number":80,"context_line":"        self.validate_exts(flow)"},{"line_number":81,"context_line":"        for task in flow:"},{"line_number":82,"context_line":"            for method, params in task.iteritems():"},{"line_number":83,"context_line":"                LOG.info(\"Executing method %s for now\" % method)"},{"line_number":84,"context_line":"                result \u003d self.execute_command(method, **params)"},{"line_number":85,"context_line":"                while not result.is_done():"}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAWH%2F9P3w%3D","line":82,"updated":"2014-04-07 17:45:07.000000000","message":"I\u0027d like to stick to .items() for python 3 compatibility.","commit_id":"a2e06066fbe68936e2735f969f0cd92b7adf5fee"},{"author":{"_account_id":10343,"name":"Jim Rollenhagen","email":"jim@jimrollenhagen.com","username":"jimrollenhagen"},"change_message_id":"559708571a7951d898f02ff937adfdac55eab076","unresolved":false,"context_lines":[{"line_number":83,"context_line":"                LOG.info(\"Executing method %s for now\" % method)"},{"line_number":84,"context_line":"                result \u003d self.execute_command(method, **params)"},{"line_number":85,"context_line":"                while not result.is_done():"},{"line_number":86,"context_line":"                    result.join(self.join_timeout)"},{"line_number":87,"context_line":"                LOG.info(\"%s method\u0027s execution is done\" % method)"},{"line_number":88,"context_line":"                if result.command_status \u003d\u003d base.AgentCommandStatus.FAILED:"},{"line_number":89,"context_line":"                    raise errors.CommandExecutionError("}],"source_content_type":"text/x-python","patch_set":2,"id":"AAAAWH%2F9PpQ%3D","line":86,"updated":"2014-04-07 17:45:07.000000000","message":"Why are we calling join() in a loop here, rather than just calling join() once?","commit_id":"a2e06066fbe68936e2735f969f0cd92b7adf5fee"},{"author":{"_account_id":3009,"name":"Vladimir Kozhukalov","email":"kozhukalov@gmail.com","username":"kozhukalov"},"change_message_id":"37646323f899de9fcd4af83ab63880b18307b497","unresolved":false,"context_lines":[{"line_number":29,"context_line":""},{"line_number":30,"context_line":"def _load_extension(ext):"},{"line_number":31,"context_line":"    disabled_extension_list \u003d [\u0027flow\u0027]"},{"line_number":32,"context_line":"    return ext.name not in disabled_extension_list"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""},{"line_number":35,"context_line":"class FlowExtension(base.BaseAgentExtension):"}],"source_content_type":"text/x-python","patch_set":3,"id":"AAAAW3%2F%2F9QI%3D","line":32,"updated":"2014-04-09 09:00:02.000000000","message":"Is that method supposed not to be able to load flow extension a priori?","commit_id":"bee1e4ec9147e1ca82b880a473c0c9b860b1df9d"},{"author":{"_account_id":8003,"name":"Aleksandr Gordeev","email":"agordeev@mirantis.com","username":"agordeev"},"change_message_id":"bf46aeefe2f8d91994572da6476e003c3f73c37e","unresolved":false,"context_lines":[{"line_number":29,"context_line":""},{"line_number":30,"context_line":"def _load_extension(ext):"},{"line_number":31,"context_line":"    disabled_extension_list \u003d [\u0027flow\u0027]"},{"line_number":32,"context_line":"    return ext.name not in disabled_extension_list"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""},{"line_number":35,"context_line":"class FlowExtension(base.BaseAgentExtension):"}],"source_content_type":"text/x-python","patch_set":3,"id":"AAAAW3%2F%2F7oY%3D","line":32,"in_reply_to":"AAAAW3%2F%2F7sc%3D","updated":"2014-04-09 12:16:22.000000000","message":"nope. Flow extension has its own extension manager which loads all available extensions once again for it.\n\nIf flow extension will be allowed to load by itself, then it will loads itself again and again recursively","commit_id":"bee1e4ec9147e1ca82b880a473c0c9b860b1df9d"},{"author":{"_account_id":3009,"name":"Vladimir Kozhukalov","email":"kozhukalov@gmail.com","username":"kozhukalov"},"change_message_id":"61ec4aff078981f95a7467e2973c763c511794e7","unresolved":false,"context_lines":[{"line_number":29,"context_line":""},{"line_number":30,"context_line":"def _load_extension(ext):"},{"line_number":31,"context_line":"    disabled_extension_list \u003d [\u0027flow\u0027]"},{"line_number":32,"context_line":"    return ext.name not in disabled_extension_list"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""},{"line_number":35,"context_line":"class FlowExtension(base.BaseAgentExtension):"}],"source_content_type":"text/x-python","patch_set":3,"id":"AAAAW3%2F%2F7sc%3D","line":32,"in_reply_to":"AAAAW3%2F%2F9HQ%3D","updated":"2014-04-09 12:04:05.000000000","message":"doesn\u0027t that mean that flow extension won\u0027t be loaded at all?","commit_id":"bee1e4ec9147e1ca82b880a473c0c9b860b1df9d"},{"author":{"_account_id":8003,"name":"Aleksandr Gordeev","email":"agordeev@mirantis.com","username":"agordeev"},"change_message_id":"5d1b52740347ab4ec35bfb75a863df5c66c1cacc","unresolved":false,"context_lines":[{"line_number":29,"context_line":""},{"line_number":30,"context_line":"def _load_extension(ext):"},{"line_number":31,"context_line":"    disabled_extension_list \u003d [\u0027flow\u0027]"},{"line_number":32,"context_line":"    return ext.name not in disabled_extension_list"},{"line_number":33,"context_line":""},{"line_number":34,"context_line":""},{"line_number":35,"context_line":"class FlowExtension(base.BaseAgentExtension):"}],"source_content_type":"text/x-python","patch_set":3,"id":"AAAAW3%2F%2F9HQ%3D","line":32,"in_reply_to":"AAAAW3%2F%2F9QI%3D","updated":"2014-04-09 09:16:18.000000000","message":"yes. Disallowing to load flow extension for the sake of avoiding flow extension loading recursively","commit_id":"bee1e4ec9147e1ca82b880a473c0c9b860b1df9d"},{"author":{"_account_id":10343,"name":"Jim Rollenhagen","email":"jim@jimrollenhagen.com","username":"jimrollenhagen"},"change_message_id":"b750ede3f6b269374911b528c379be018be5d409","unresolved":false,"context_lines":[{"line_number":44,"context_line":"        )"},{"line_number":45,"context_line":"        self.command_lock \u003d threading.Lock()"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":"    def execute_command(self, command_name, **kwargs):"},{"line_number":48,"context_line":"        \"\"\"Execute an agent command.\"\"\""},{"line_number":49,"context_line":"        with self.command_lock:"},{"line_number":50,"context_line":"            extension_part, command_part \u003d utils.split_command(command_name)"}],"source_content_type":"text/x-python","patch_set":3,"id":"AAAAWn%2F%2F6%2F0%3D","line":47,"updated":"2014-04-08 13:46:09.000000000","message":"Sorry I missed this the first time, but this code is very similar to the method on the agent class. Is it possible to break the common code into utils.py?","commit_id":"bee1e4ec9147e1ca82b880a473c0c9b860b1df9d"},{"author":{"_account_id":8003,"name":"Aleksandr Gordeev","email":"agordeev@mirantis.com","username":"agordeev"},"change_message_id":"c20a44964eacdbe7eaa984588f7f0e7e3b510ebb","unresolved":false,"context_lines":[{"line_number":44,"context_line":"        )"},{"line_number":45,"context_line":"        self.command_lock \u003d threading.Lock()"},{"line_number":46,"context_line":""},{"line_number":47,"context_line":"    def execute_command(self, command_name, **kwargs):"},{"line_number":48,"context_line":"        \"\"\"Execute an agent command.\"\"\""},{"line_number":49,"context_line":"        with self.command_lock:"},{"line_number":50,"context_line":"            extension_part, command_part \u003d utils.split_command(command_name)"}],"source_content_type":"text/x-python","patch_set":3,"id":"AAAAWn%2F%2F6uc%3D","line":47,"in_reply_to":"AAAAWn%2F%2F6%2F0%3D","updated":"2014-04-08 14:13:52.000000000","message":"Surely possible. I\u0027ll do it","commit_id":"bee1e4ec9147e1ca82b880a473c0c9b860b1df9d"},{"author":{"_account_id":10343,"name":"Jim Rollenhagen","email":"jim@jimrollenhagen.com","username":"jimrollenhagen"},"change_message_id":"b750ede3f6b269374911b528c379be018be5d409","unresolved":false,"context_lines":[{"line_number":71,"context_line":"            for method in task:"},{"line_number":72,"context_line":"                ext_name \u003d utils.split_command(method)[0]"},{"line_number":73,"context_line":"                if ext_name not in self.flow_mgr.names():"},{"line_number":74,"context_line":"                    raise errors.InvalidCommandParamsError("},{"line_number":75,"context_line":"                        \u0027Extension %s is unavailabe. Aborting flow\u0027 % ext_name)"},{"line_number":76,"context_line":""},{"line_number":77,"context_line":"    @decorators.async_command()"}],"source_content_type":"text/x-python","patch_set":3,"id":"AAAAWn%2F%2F6%2Bs%3D","line":74,"updated":"2014-04-08 13:46:09.000000000","message":"This should probably be a RequestedObjectNotFoundError like in the agent\u0027s execute_command error.\n\nAlso, maybe we want to validate that the extension has the command requested? Maybe just check hasattr and if it is callable?","commit_id":"bee1e4ec9147e1ca82b880a473c0c9b860b1df9d"},{"author":{"_account_id":8003,"name":"Aleksandr Gordeev","email":"agordeev@mirantis.com","username":"agordeev"},"change_message_id":"c20a44964eacdbe7eaa984588f7f0e7e3b510ebb","unresolved":false,"context_lines":[{"line_number":71,"context_line":"            for method in task:"},{"line_number":72,"context_line":"                ext_name \u003d utils.split_command(method)[0]"},{"line_number":73,"context_line":"                if ext_name not in self.flow_mgr.names():"},{"line_number":74,"context_line":"                    raise errors.InvalidCommandParamsError("},{"line_number":75,"context_line":"                        \u0027Extension %s is unavailabe. Aborting flow\u0027 % ext_name)"},{"line_number":76,"context_line":""},{"line_number":77,"context_line":"    @decorators.async_command()"}],"source_content_type":"text/x-python","patch_set":3,"id":"AAAAWn%2F%2F6vY%3D","line":74,"in_reply_to":"AAAAWn%2F%2F6%2Bs%3D","updated":"2014-04-08 14:13:52.000000000","message":"that makes sense. Thanks","commit_id":"bee1e4ec9147e1ca82b880a473c0c9b860b1df9d"},{"author":{"_account_id":3009,"name":"Vladimir Kozhukalov","email":"kozhukalov@gmail.com","username":"kozhukalov"},"change_message_id":"b3e55f733e29cf1930913f79ad7e436d26a176fe","unresolved":false,"context_lines":[{"line_number":51,"context_line":"                    ext_obj \u003d self.ext_mgr[ext].obj"},{"line_number":52,"context_line":"                    if not (hasattr(ext_obj, \u0027execute\u0027) and"},{"line_number":53,"context_line":"                      hasattr(ext_obj, \u0027command_map\u0027) and"},{"line_number":54,"context_line":"                      cmd in ext_obj.command_map):"},{"line_number":55,"context_line":"                        raise errors.InvalidCommandParamsError("},{"line_number":56,"context_line":"                            \"Extension {0} doesn\u0027t provide {1} method\"."},{"line_number":57,"context_line":"                                format(ext, cmd))"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAW3%2F%2F3eI%3D","line":54,"updated":"2014-04-09 20:13:31.000000000","message":"Looks like part of this validation \u0027hasattr(ext_obj, \u0027execute\u0027)\u0027 should be inside ExecuteCommandMixin and another part \u0027hasattr(ext_obj, \u0027command_map\u0027) and cmd in ext_obj.command_map\u0027 should be a part of BaseAgentExtension validation program.\nI think so because Flow does not need to make any assumptions about execute_command internal implementation (I mean calling execute method). And it strange to enforce internal extension execute method to be implemented like it is in BaseAgentExtension (I mean having command_map attribute and searching necessary command there).","commit_id":"49a19b5fcabf54eb490b0eb0c2a4a817f8a25a2e"},{"author":{"_account_id":8003,"name":"Aleksandr Gordeev","email":"agordeev@mirantis.com","username":"agordeev"},"change_message_id":"20202dcfdd445b366bf6701bfcbd686b34bd0692","unresolved":false,"context_lines":[{"line_number":51,"context_line":"                    ext_obj \u003d self.ext_mgr[ext].obj"},{"line_number":52,"context_line":"                    if not (hasattr(ext_obj, \u0027execute\u0027) and"},{"line_number":53,"context_line":"                      hasattr(ext_obj, \u0027command_map\u0027) and"},{"line_number":54,"context_line":"                      cmd in ext_obj.command_map):"},{"line_number":55,"context_line":"                        raise errors.InvalidCommandParamsError("},{"line_number":56,"context_line":"                            \"Extension {0} doesn\u0027t provide {1} method\"."},{"line_number":57,"context_line":"                                format(ext, cmd))"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAW3%2F%2FywM%3D","line":54,"in_reply_to":"AAAAW3%2F%2F3eI%3D","updated":"2014-04-10 11:32:50.000000000","message":"It\u0027s just advanced validation which does deeper extension inspection. \n\nFlowExtension needs to be sure that it can execute task in flow. In other words it looks if task is referring to existent extension and has the callable method.\n\nThe reason is to know first if any task probably couldn\u0027t be executed, so the flow won\u0027t be started.","commit_id":"49a19b5fcabf54eb490b0eb0c2a4a817f8a25a2e"},{"author":{"_account_id":3009,"name":"Vladimir Kozhukalov","email":"kozhukalov@gmail.com","username":"kozhukalov"},"change_message_id":"c8257870ff1ea5220b885ddf2e0f41f4f0cc82eb","unresolved":false,"context_lines":[{"line_number":57,"context_line":"                                format(ext, cmd))"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    @decorators.async_command()"},{"line_number":60,"context_line":"    def start_flow(self, command_name, flow\u003dNone):"},{"line_number":61,"context_line":"        self.validate_exts(flow)"},{"line_number":62,"context_line":"        for task in flow:"},{"line_number":63,"context_line":"            for method, params in task.items():"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAW3%2F%2F3qw%3D","line":60,"updated":"2014-04-09 19:34:24.000000000","message":"I don\u0027t see the reason why we need here this argument \u0027command_name\u0027. Actually, there three commands in standby extension and they also don\u0027t need this argument.","commit_id":"49a19b5fcabf54eb490b0eb0c2a4a817f8a25a2e"},{"author":{"_account_id":10626,"name":"Russell Haering","email":"russellhaering@gmail.com","username":"russell_h"},"change_message_id":"6d73729c341fc6f3ec408513a2aee760ca15e992","unresolved":false,"context_lines":[{"line_number":57,"context_line":"                                format(ext, cmd))"},{"line_number":58,"context_line":""},{"line_number":59,"context_line":"    @decorators.async_command()"},{"line_number":60,"context_line":"    def start_flow(self, command_name, flow\u003dNone):"},{"line_number":61,"context_line":"        self.validate_exts(flow)"},{"line_number":62,"context_line":"        for task in flow:"},{"line_number":63,"context_line":"            for method, params in task.items():"}],"source_content_type":"text/x-python","patch_set":7,"id":"AAAAW3%2F%2F3kg%3D","line":60,"in_reply_to":"AAAAW3%2F%2F3qw%3D","updated":"2014-04-09 19:53:46.000000000","message":"The \u0027command_name\u0027 argument used to be needed in order to construct an AsyncCommandResult. Now that we have the async_command decorator I\u0027d be +1 to deprecating it.\n\nThat being said, we should do so in a separate change.","commit_id":"49a19b5fcabf54eb490b0eb0c2a4a817f8a25a2e"},{"author":{"_account_id":10343,"name":"Jim Rollenhagen","email":"jim@jimrollenhagen.com","username":"jimrollenhagen"},"change_message_id":"5bf37c4b9e153e99f9f0206def4034105b9d4869","unresolved":false,"context_lines":[{"line_number":47,"context_line":"                ext, cmd \u003d agent_utils.split_command(method)"},{"line_number":48,"context_line":"                if ext not in self.ext_mgr.names():"},{"line_number":49,"context_line":"                    raise errors.RequestedObjectNotFoundError(\u0027Extension\u0027, ext)"},{"line_number":50,"context_line":"                else:"},{"line_number":51,"context_line":"                    ext_obj \u003d self.ext_mgr[ext].obj"},{"line_number":52,"context_line":"                    if not (hasattr(ext_obj, \u0027execute\u0027) and"},{"line_number":53,"context_line":"                      hasattr(ext_obj, \u0027command_map\u0027) and"}],"source_content_type":"text/x-python","patch_set":11,"id":"AAAAW3%2F%2FtcQ%3D","line":50,"updated":"2014-04-10 23:33:12.000000000","message":"nitpick: this else is redundant","commit_id":"595d04bfe5d31e1d7ac364ae81f76742d8ec9f26"},{"author":{"_account_id":8003,"name":"Aleksandr Gordeev","email":"agordeev@mirantis.com","username":"agordeev"},"change_message_id":"407199e021469690fc58f8de81229d73d2bc4e3d","unresolved":false,"context_lines":[{"line_number":47,"context_line":"                ext, cmd \u003d agent_utils.split_command(method)"},{"line_number":48,"context_line":"                if ext not in self.ext_mgr.names():"},{"line_number":49,"context_line":"                    raise errors.RequestedObjectNotFoundError(\u0027Extension\u0027, ext)"},{"line_number":50,"context_line":"                else:"},{"line_number":51,"context_line":"                    ext_obj \u003d self.ext_mgr[ext].obj"},{"line_number":52,"context_line":"                    if not (hasattr(ext_obj, \u0027execute\u0027) and"},{"line_number":53,"context_line":"                      hasattr(ext_obj, \u0027command_map\u0027) and"}],"source_content_type":"text/x-python","patch_set":11,"id":"AAAAW3%2F%2FpH4%3D","line":50,"in_reply_to":"AAAAW3%2F%2FtcQ%3D","updated":"2014-04-11 13:21:59.000000000","message":"Done","commit_id":"595d04bfe5d31e1d7ac364ae81f76742d8ec9f26"},{"author":{"_account_id":3009,"name":"Vladimir Kozhukalov","email":"kozhukalov@gmail.com","username":"kozhukalov"},"change_message_id":"f0e010d0e31998a818fbbc6dc28911baed23430b","unresolved":false,"context_lines":[{"line_number":51,"context_line":"                    ext_obj \u003d self.ext_mgr[ext].obj"},{"line_number":52,"context_line":"                    if not (hasattr(ext_obj, \u0027execute\u0027) and"},{"line_number":53,"context_line":"                      hasattr(ext_obj, \u0027command_map\u0027) and"},{"line_number":54,"context_line":"                      cmd in ext_obj.command_map):"},{"line_number":55,"context_line":"                        raise errors.InvalidCommandParamsError("},{"line_number":56,"context_line":"                            \"Extension {0} doesn\u0027t provide {1} method\"."},{"line_number":57,"context_line":"                                format(ext, cmd))"}],"source_content_type":"text/x-python","patch_set":11,"id":"AAAAW3%2F%2FrGQ%3D","line":54,"updated":"2014-04-11 08:42:29.000000000","message":"I still think checking implementation dependent attributes here is an example of a not very good design. execute_command method calls execute method of extension and this piece of validation should be implemented as a part of mixin not a part of this particular class. FlowExtension should know nothing about how execute_method calles extensions.\nIn turn, command_map attribute is nothing less than implementation details of execute method of BaseAgentExtension. So FlowExtension again should not make any assumptions about execute method internal details.\nThis code will work well as long as execute method is not going to be changed.","commit_id":"595d04bfe5d31e1d7ac364ae81f76742d8ec9f26"},{"author":{"_account_id":10343,"name":"Jim Rollenhagen","email":"jim@jimrollenhagen.com","username":"jimrollenhagen"},"change_message_id":"5bf37c4b9e153e99f9f0206def4034105b9d4869","unresolved":false,"context_lines":[{"line_number":51,"context_line":"                    ext_obj \u003d self.ext_mgr[ext].obj"},{"line_number":52,"context_line":"                    if not (hasattr(ext_obj, \u0027execute\u0027) and"},{"line_number":53,"context_line":"                      hasattr(ext_obj, \u0027command_map\u0027) and"},{"line_number":54,"context_line":"                      cmd in ext_obj.command_map):"},{"line_number":55,"context_line":"                        raise errors.InvalidCommandParamsError("},{"line_number":56,"context_line":"                            \"Extension {0} doesn\u0027t provide {1} method\"."},{"line_number":57,"context_line":"                                format(ext, cmd))"}],"source_content_type":"text/x-python","patch_set":11,"id":"AAAAW3%2F%2Ftbw%3D","line":54,"updated":"2014-04-10 23:33:12.000000000","message":"Maybe it\u0027s just me, but I find this style hard to read - I think the extra lines should be indented by 8 characters more that the beginning of the if statement.","commit_id":"595d04bfe5d31e1d7ac364ae81f76742d8ec9f26"},{"author":{"_account_id":8003,"name":"Aleksandr Gordeev","email":"agordeev@mirantis.com","username":"agordeev"},"change_message_id":"407199e021469690fc58f8de81229d73d2bc4e3d","unresolved":false,"context_lines":[{"line_number":51,"context_line":"                    ext_obj \u003d self.ext_mgr[ext].obj"},{"line_number":52,"context_line":"                    if not (hasattr(ext_obj, \u0027execute\u0027) and"},{"line_number":53,"context_line":"                      hasattr(ext_obj, \u0027command_map\u0027) and"},{"line_number":54,"context_line":"                      cmd in ext_obj.command_map):"},{"line_number":55,"context_line":"                        raise errors.InvalidCommandParamsError("},{"line_number":56,"context_line":"                            \"Extension {0} doesn\u0027t provide {1} method\"."},{"line_number":57,"context_line":"                                format(ext, cmd))"}],"source_content_type":"text/x-python","patch_set":11,"id":"AAAAW3%2F%2FpHs%3D","line":54,"in_reply_to":"AAAAW3%2F%2FrGQ%3D","updated":"2014-04-11 13:21:59.000000000","message":"Done","commit_id":"595d04bfe5d31e1d7ac364ae81f76742d8ec9f26"},{"author":{"_account_id":8003,"name":"Aleksandr Gordeev","email":"agordeev@mirantis.com","username":"agordeev"},"change_message_id":"407199e021469690fc58f8de81229d73d2bc4e3d","unresolved":false,"context_lines":[{"line_number":51,"context_line":"                    ext_obj \u003d self.ext_mgr[ext].obj"},{"line_number":52,"context_line":"                    if not (hasattr(ext_obj, \u0027execute\u0027) and"},{"line_number":53,"context_line":"                      hasattr(ext_obj, \u0027command_map\u0027) and"},{"line_number":54,"context_line":"                      cmd in ext_obj.command_map):"},{"line_number":55,"context_line":"                        raise errors.InvalidCommandParamsError("},{"line_number":56,"context_line":"                            \"Extension {0} doesn\u0027t provide {1} method\"."},{"line_number":57,"context_line":"                                format(ext, cmd))"}],"source_content_type":"text/x-python","patch_set":11,"id":"AAAAW3%2F%2FpH0%3D","line":54,"in_reply_to":"AAAAW3%2F%2Ftbw%3D","updated":"2014-04-11 13:21:59.000000000","message":"Done","commit_id":"595d04bfe5d31e1d7ac364ae81f76742d8ec9f26"},{"author":{"_account_id":10626,"name":"Russell Haering","email":"russellhaering@gmail.com","username":"russell_h"},"change_message_id":"22ad9a1523679db6b3564c2f9af77e96c1811f40","unresolved":false,"context_lines":[{"line_number":51,"context_line":""},{"line_number":52,"context_line":"    @decorators.async_command()"},{"line_number":53,"context_line":"    def start_flow(self, command_name, flow\u003dNone):"},{"line_number":54,"context_line":"        self.validate_exts(flow)"},{"line_number":55,"context_line":"        for task in flow:"},{"line_number":56,"context_line":"            for method, params in task.items():"},{"line_number":57,"context_line":"                LOG.info(\"Executing method %s for now\" % method)"}],"source_content_type":"text/x-python","patch_set":12,"id":"AAAAW3%2F%2Fkp8%3D","line":54,"updated":"2014-04-12 06:48:39.000000000","message":"Something to be aware of here: because this is an async command, once the \u0027start_flow\u0027 method starts, the agent will return to the caller. Any exception raised inside this method will cause the command to fail, but the caller will need to poll the command status at least once to see this.\n\nIf you pass a validator to the async_command decorator, you can raise an error before the first call returns, but you can\u0027t pass a method on the extension itself (this being, to my knowledge, a limitation of Python decorators, although I could be wrong), so won\u0027t have access to \u0027self\u0027 (and the associated extension manager).\n\nI think it\u0027d be reasonable to ship this as-is so you can begin implementing things against it, then move the validation up to happen synchronously during the call later, but its your call.","commit_id":"32f865ac6eff0595e1498ef43d4c49e66d731172"},{"author":{"_account_id":10626,"name":"Russell Haering","email":"russellhaering@gmail.com","username":"russell_h"},"change_message_id":"eab58285f29853965b4871217afe3fdbe941264e","unresolved":false,"context_lines":[{"line_number":27,"context_line":"    return ext.name not in disabled_extension_list"},{"line_number":28,"context_line":""},{"line_number":29,"context_line":""},{"line_number":30,"context_line":"def _validate_exts(self, flow\u003dNone):"},{"line_number":31,"context_line":"    for task in flow:"},{"line_number":32,"context_line":"        for method in task:"},{"line_number":33,"context_line":"            ext, cmd \u003d self.split_command(method)"}],"source_content_type":"text/x-python","patch_set":13,"id":"AAAAW3%2F%2FcIA%3D","line":30,"updated":"2014-04-14 15:23:20.000000000","message":"Same thought here about not calling it self","commit_id":"19b24e418e138437e8749303628055d4050a6060"}]}
